diff --git a/.gitignore b/.gitignore index cd2641c22..6cebf87fe 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ src/test/test_bitcoin *zcashTest.vk # autoreconf -Makefile.in +#Makefile.in aclocal.m4 autom4te.cache/ build-aux/config.guess @@ -29,7 +29,7 @@ build-aux/compile build-aux/test-driver config.log config.status -configure +#configure libtool src/config/bitcoin-config.h src/config/bitcoin-config.h.in @@ -126,3 +126,32 @@ src/komodo-tx.exe src/cryptoconditions/compile + +src/cc/rogue/rogue + +src/cc/rogue/rogue.so + +src/cc/rogue/test.zip +src/checkfile + +src/foo.zip + +src/log + +src/rogue.530623577502174316.0 + +src/rogue.530623577502174316.pack + +src/rogue.530623577502174316.player + + +src/cc/rogue/config.h + +src/cc/rogue/config.h + +src/ROGUE.conf + +src/rogue.scr + +src/cc/rogue/confdefs.h +src/cc/rogue/x64 diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3ace05cb9..a7354b240 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,9 +32,9 @@ build:ubuntu: variables: DOCKER_DRIVER: overlay2 cache: - key: "${CI_JOB_NAME}${CI_COMMIT_REF_NAME}" + key: ${CI_COMMIT_REF_SLUG} paths: - - depends/built + - depends/ script: - zcutil/build.sh -j$(nproc) - mkdir ${PACKAGE_DIR_LINUX} @@ -71,6 +71,10 @@ build:windows: - cp src/komodod.exe src/komodo-cli.exe src/komodo-tx.exe + src/cc/rogue/rogue.exe + zcutil/fetch-params.bat + src/cc/rogue/x86_64-w64-mingw32/bin/libcurl-4.dll + src/cc/rogue/x86_64-w64-mingw32/bin/libncursesw6.dll ${PACKAGE_DIR_WINDOWS} - zip -r ${PACKAGE_DIR_WINDOWS}.zip ${PACKAGE_DIR_WINDOWS} - md5sum ${AGAMA_ARTIFACTS_WINDOWS} > ${AGAMA_ARTIFACTS_WINDOWS_CHECKSUM} @@ -91,6 +95,7 @@ build:macos: key: "${CI_JOB_NAME}${CI_COMMIT_REF_NAME}" paths: - depends/built + allow_failure: true script: - zcutil/build-mac.sh -j$(sysctl -n hw.physicalcpu) - ./makeRelease.sh ${PACKAGE_DIR_MACOS} diff --git a/README.md b/README.md index 9a8582891..6c0002ba4 100644 --- a/README.md +++ b/README.md @@ -71,16 +71,32 @@ cd komodo #This can take some time. ``` + #### OSX -Ensure you have [brew](https://brew.sh) and the command line tools installed (comes automatically with XCode) and run: +Ensure you have [brew](https://brew.sh) and Command Line Tools installed. ```shell -brew update && brew install gcc@6 +# Install brew +/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" +# Install Xcode, opens a pop-up window to install CLT without installing the entire Xcode package +xcode-select --install +# Update brew and install dependencies +brew update +brew upgrade +brew tap discoteq/discoteq; brew install flock +brew install autoconf autogen automake +brew install gcc@6 +brew install binutils +brew install protobuf +brew install coreutils +brew install wget +# Clone the Komodo repo git clone https://github.com/komodoplatform/komodo --branch master --single-branch +# Change master branch to other branch you wish to compile cd komodo ./zcutil/fetch-params.sh # -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. +# This can take some time. ``` #### Windows diff --git a/configure.ac b/configure.ac index 9a1a75f53..12256c0cc 100644 --- a/configure.ac +++ b/configure.ac @@ -884,6 +884,7 @@ fi AM_CONDITIONAL([TARGET_DARWIN], [test x$TARGET_OS = xdarwin]) AM_CONDITIONAL([BUILD_DARWIN], [test x$BUILD_OS = xdarwin]) +AM_CONDITIONAL([TARGET_LINUX], [test x$TARGET_OS = xlinux]) AM_CONDITIONAL([TARGET_WINDOWS], [test x$TARGET_OS = xwindows]) AM_CONDITIONAL([ENABLE_WALLET],[test x$enable_wallet = xyes]) AM_CONDITIONAL([ENABLE_MINING],[test x$enable_mining = xyes]) diff --git a/depends/packages/boost.mk b/depends/packages/boost.mk index 8f3fd1b1c..800c424c5 100644 --- a/depends/packages/boost.mk +++ b/depends/packages/boost.mk @@ -1,9 +1,9 @@ - package=boost + $(package)_version=1_66_0 $(package)_download_path=https://dl.bintray.com/boostorg/release/1.66.0/source -$(package)_file_name=$(package)_$($(package)_version).tar.bz2 $(package)_sha256_hash=5721818253e6a0989583192f96782c4a98eb6204965316df9f5ad75819225ca9 +$(package)_file_name=$(package)_$($(package)_version).tar.bz2 define $(package)_set_vars $(package)_config_opts_release=variant=release diff --git a/depends/packages/libsodium.mk b/depends/packages/libsodium.mk index 76f0d9a28..179ebe86c 100644 --- a/depends/packages/libsodium.mk +++ b/depends/packages/libsodium.mk @@ -1,12 +1,3 @@ -ifeq ($(build_os),darwin) -package=libsodium -$(package)_version=1.0.11 -$(package)_download_path=https://supernetorg.bintray.com/misc -$(package)_file_name=libsodium-1.0.11.tar.gz -$(package)_sha256_hash=a14549db3c49f6ae2170cbbf4664bd48ace50681045e8dbea7c8d9fb96f9c765 -$(package)_dependencies= -$(package)_config_opts= -else package=libsodium $(package)_version=1.0.15 $(package)_download_path=https://download.libsodium.org/libsodium/releases/old @@ -14,6 +5,13 @@ $(package)_file_name=$(package)-$($(package)_version).tar.gz $(package)_sha256_hash=fb6a9e879a2f674592e4328c5d9f79f082405ee4bb05cb6e679b90afe9e178f4 $(package)_dependencies= $(package)_config_opts= + +ifeq ($(build_os),darwin) +define $(package)_set_vars + $(package)_build_env=MACOSX_DEPLOYMENT_TARGET="10.11" + $(package)_cc=clang + $(package)_cxx=clang++ +endef endif define $(package)_preprocess_cmds diff --git a/depends/packages/openssl.mk b/depends/packages/openssl.mk index dec8ecef6..e378088e6 100644 --- a/depends/packages/openssl.mk +++ b/depends/packages/openssl.mk @@ -1,8 +1,8 @@ package=openssl -$(package)_version=1.1.0h +$(package)_version=1.1.1a $(package)_download_path=https://www.openssl.org/source $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=5835626cde9e99656585fc7aaa2302a73a7e1340bf8c14fd635a62c66802a517 +$(package)_sha256_hash=fc20130f8b7cbd2fb918b2f14e2f429e109c31ddd0fb38fc5d71d9ffed3f9f41 define $(package)_set_vars $(package)_config_env=AR="$($(package)_ar)" RANLIB="$($(package)_ranlib)" CC="$($(package)_cc)" @@ -92,7 +92,7 @@ $(package)_config_opts_i686_mingw32=mingw endef define $(package)_preprocess_cmds - sed -i.old "/define DATE/d" util/mkbuildinf.pl && \ + sed -i.old 's/built on: $date/built on: not available/' util/mkbuildinf.pl && \ sed -i.old "s|\"engines\", \"apps\", \"test\"|\"engines\"|" Configure endef diff --git a/depends/packages/rust.mk b/depends/packages/rust.mk index a08ac2747..9cfb95054 100644 --- a/depends/packages/rust.mk +++ b/depends/packages/rust.mk @@ -1,13 +1,13 @@ package=rust -$(package)_version=1.28.0 +$(package)_version=1.32.0 $(package)_download_path=https://static.rust-lang.org/dist $(package)_file_name_linux=rust-$($(package)_version)-x86_64-unknown-linux-gnu.tar.gz -$(package)_sha256_hash_linux=2a1390340db1d24a9498036884e6b2748e9b4b057fc5219694e298bdaa37b810 +$(package)_sha256_hash_linux=e024698320d76b74daf0e6e71be3681a1e7923122e3ebd03673fcac3ecc23810 $(package)_file_name_darwin=rust-$($(package)_version)-x86_64-apple-darwin.tar.gz -$(package)_sha256_hash_darwin=5d7a70ed4701fe9410041c1eea025c95cad97e5b3d8acc46426f9ac4f9f02393 +$(package)_sha256_hash_darwin=f0dfba507192f9b5c330b5984ba71d57d434475f3d62bd44a39201e36fa76304 $(package)_file_name_mingw32=rust-$($(package)_version)-x86_64-pc-windows-gnu.tar.gz -$(package)_sha256_hash_mingw32=55c07426f791c51c8a2b6934b35784175c4abb4e03f123f3e847109c4dc1ad8b +$(package)_sha256_hash_mingw32=358e1435347c67dbf33aa9cad6fe501a833d6633ed5d5aa1863d5dffa0349be9 ifeq ($(build_os),darwin) $(package)_file_name=$($(package)_file_name_darwin) diff --git a/depends/packages/zeromq.mk b/depends/packages/zeromq.mk index b9a57cba7..89fb6ef1c 100644 --- a/depends/packages/zeromq.mk +++ b/depends/packages/zeromq.mk @@ -1,23 +1,23 @@ ifeq ($(host_os),mingw32) -$(package)_version=4.2.2-1 +$(package)_version=4.3.1 $(package)_download_path=https://github.com/ca333/libzmq/archive $(package)_download_file=v$($(package)_version).tar.gz $(package)_file_name=libzmq-$($(package)_version).tar.gz -$(package)_sha256_hash=0e225b85ce11be23bf7eb7d3f25c6686728bf30d5c31f61c12d37bb646c69962 +$(package)_sha256_hash=cb8ebe5b60dadeb526745610d6237f05a98aba287114d8991dad1fa14f4be354 define $(package)_set_vars $(package)_build_env+= $(package)_config_opts=--enable-shared=false --enable-static --host=x86_64-w64-mingw32 - $(package)_config_opts_mingw32=--enable-shared=false --enable-static --host=x86_64-w64-mingw32 + $(package)_config_opts_mingw32=--enable-shared=false --enable-static --prefix=$(host_prefix) --host=x86_64-w64-mingw32 -disable-curve $(package)_cflags=-Wno-error -Wall -Wno-pedantic-ms-format -DLIBCZMQ_EXPORTS -DZMQ_DEFINED_STDINT -lws2_32 -liphlpapi -lrpcrt4 $(package)_conf_tool=./configure endef else package=zeromq -$(package)_version=4.2.1 +$(package)_version=4.3.1 $(package)_download_path=https://github.com/zeromq/libzmq/releases/download/v$($(package)_version) $(package)_file_name=$(package)-$($(package)_version).tar.gz -$(package)_sha256_hash=27d1e82a099228ee85a7ddb2260f40830212402c605a4a10b5e5498a7e0e9d03 +$(package)_sha256_hash=bcbabe1e2c7d0eec4ed612e10b94b112dd5f06fcefa994a0c79a45d835cd21eb define $(package)_set_vars $(package)_config_opts=--without-documentation --disable-shared --disable-curve diff --git a/doc/release-notes/release-notes-0.3.0.md b/doc/release-notes/release-notes-0.3.0.md new file mode 100644 index 000000000..84ffc8efb --- /dev/null +++ b/doc/release-notes/release-notes-0.3.0.md @@ -0,0 +1,58 @@ + +Komodo specific changelog: + +- add CC functionality and bugfixes +- set sapling activation +- change z-addr prefix byte +- blocksize increased from 2MB to 4 MB +- transaction size increased from 100KB to 200KB + +Sapling related changelog: + +- Decoupled Spend Authority +- Improved Performance for Shielded Transactions (using sapling instead of sprout) +- transaction format changed + +Alex Morcos (1): Output line to debug.log when IsInitialBlockDownload latches to false + +Ariel Gabizon (1): Extend Joinsplit tests to Groth + +Charlie OKeefe (1): Remove extra slash from lockfile path + +Cory Fields (1): crypter: shuffle Makefile so that crypto can be used by the wallet + +Daira Hopwood (1): Support testnet rollback. + +Daniel Cousens (2): move rpc* to rpc/ rpc: update inline comments to refer to new file paths + +Dimitris Apostolou (1): Fix typos + +Duke Leto (3): Fix absurd fee bug reported in #3281, with tests Update comment as per @arielgabizon Improve error message + +Eirik Ogilvie-Wigley (24): Add more options when asserting in RPC tests Add change indicator for notes Fix test broken by change indicator Rename note data to include sprout Remove redundant curly braces Consolidate for loops Add out point for sapling note data Add sapling note data and map Decrement sapling note witnesses Clear sapling witness cache Extract method for copying previous witnesses Extract methods for incrementing witnesses Extract method for incrementing witness heights Pass sapling merkle tree when incrementing witnesses Increment sapling note witnesses Rename sprout specific methods Remove extra indentation Add getter and setter for sapling note data and update tests Add parameter for version in GetValidReceive Rename Merkle Trees to include sprout or sapling Rename Witnesses to include sprout or sapling Rename test objects to include sprout or sapling Only include the change field if we have a spending key Fix assertion and comment + +Gregory Maxwell (2): IBD check uses minimumchain work instead of checkpoints. IsInitialBlockDownload no longer uses header-only timestamps. + +Jack Grigg (41): Add some more checkpoints, up to the 1.1.0 release Add Sapling support to z_validateaddress Update payment-api.md with type field of z_validateaddress Alter SaplingNote::nullifier() to take a SaplingFullViewingKey Expose note position in IncrementalMerkleWitness TransactionBuilder with support for creating Sapling-only transactions TransactionBuilder: Check that all anchors in a transaction are identical Formatting test: Move ECC_Start() call into src/gtest/main.cpp TransactionBuilder: Add support for transparent inputs and outputs TransactionBuilder: Add change output to transaction TransactionBuilder: Make fee configurable Rename xsk to expsk Implement CKeyStore::GetSaplingPaymentAddresses() Raise the 90-character limit on Bech32 encodings Add Sapling support to z_getnewaddress and z_listaddresses Fix block hash for checkpoint at height 270000 Formatting test: Deduplicate logic in wallet_addresses RPC test test: Another assert in wallet_zkeys_tests.store_and_load_sapling_zkeys test: Fix permissions of wallet_addresses test: Update rpc_wallet_z_importexport to account for Sapling changes Rename DecryptSpendingKey -> DecryptSproutSpendingKey Rename CryptedSpendingKeyMap -> CryptedSproutSpendingKeyMap Add Sapling decryption check to CCryptoKeyStore::Unlock() Check for unencrypted Sapling keys in CCryptoKeyStore::SetCrypted() Remove outdated comment Add CWallet::AddCryptedSaplingSpendingKey() hook Pass SaplingPaymentAddress to store through the CKeyStore Rename SpendingKeyMap -> SproutSpendingKeyMap Rename SerializedSize -> SerializedSproutSize Rename ViewingKey -> SproutViewingKey Formatting nits Rename *SpendingKey -> *SproutSpendingKey chainparams: Add BIP 44 coin type (as registered in SLIP 44) Upgrade Rust to 1.28.0 stable Adjust Makefile so that common can be used by the wallet Move RewindBlockIndex log message inside rewindLength check test: gtest for Sapling encoding and decoding test: Use regtest in key_tests/zs_address_test Disable Sapling features on mainnet + +Jay Graber (13): Add Sapling Add/Have/Get to keystore Add SaplingIncomingViewingKeys map, SaplingFullViewingKey methods Add StoreAndRetrieveSaplingSpendingKey test Change default_address to return SaplingPaymentAddr and not boost::optional Add crypted keystore sapling add key Discard sk if ivk == 0 Add Sapling support to z_exportkey Add Sapling support to z_importkey Add Sapling to rpc_wallet_z_importexport test Refactor into visitors and throw errors for invalid key or address. Take expiryheight as param to createrawtransaction Add Sapling have/get sk crypter overrides Add Sapling keys to CCryptoKeyStore::EncryptKeys + +Jonas Schnelli (2): [RPC, Wallet] Move RPC dispatch table registration to wallet/ code Fix test_bitcoin circular dependency issue + +Kaz Wesley (1): IsInitialBlockDownload: usually avoid locking + +Larry Ruane (4): Disable libsnark debug logging in Boost tests add extra help how to enable experimental features Add call to sync_all() after (z_sendmany, wait) don't ban peers when loading pre-overwinter blocks + +Pejvan (2): Update README.md Update README.md + +Richard Littauer (1): docs(LICENSE): update license year to 2018 + +Sean Bowe (21): Update librustzcash Implementation of Sapling in-band secret distribution. Swap types in OutputDescription to use new NoteEncryption interfaces. Prevent nonce reuse in Sapling note encryption API. Add get_esk() function to Sapling note encryption. Minor edits Decryption and tests of note/outgoing encryption. Update librustzcash and sapling-crypto. Fix bug in return value. Ensure sum of valueBalance and all vpub_new's does not exceed MAX_MONEY inside of CheckTransactionWithoutProofVerification. Move extern params to beginning of test_checktransaction. Relocate ECC_Start() to avoid test failures. Don't call ECC_Start/ECC_Stop outside the test harness. Make changes to gtest ECC behavior suggested by @str4d. Check the hash of the (Sapling+) zk-SNARK parameters during initialization. Switch to use the official Sapling parameters. make-release.py: Versioning changes for 2.0.0-rc1. make-release.py: Updated manpages for 2.0.0-rc1. make-release.py: Updated release notes and changelog for 2.0.0-rc1. Always write the empty root down as the best root, since we may roll back. Sapling mainnet activation height + +Simon Liu (11): Add encryption of SaplingNotePlaintext and SaplingOutgoingPlaintext classes. Update and fix per review comments, the test for absurd fee. Minor update to address nits in review. Implement Sapling note decryption using full viewing key. Rename AttemptSaplingEncDecryptionUsingFullViewingKey and use function overloading. Only check for a valid Sapling anchor after Sapling activation. Clean up for rebase: rename mapNoteData to mapSproutNoteData. Clean up help messages for RPC createrawtransaction. Add tests for expiryheight parameter of RPC createrawtransaction. make-release.py: Versioning changes for 2.0.0. make-release.py: Updated manpages for 2.0.0. + +Wladimir J. van der Laan (2): Make max tip age an option instead of chainparam rpc: Register calls where they are defined + +kozyilmaz (1): Add -Wl,-pie linker option for macOS and use it instead of -pie + +mdr0id (1): Fix minor references to auto-senescence in code diff --git a/qa/pull-tester/rpc-tests.sh b/qa/pull-tester/rpc-tests.sh index 226c16b11..c4775fa85 100755 --- a/qa/pull-tester/rpc-tests.sh +++ b/qa/pull-tester/rpc-tests.sh @@ -11,6 +11,8 @@ export BITCOIND=${REAL_BITCOIND} #Run the tests testScripts=( + 'dpow.py' + 'dpowconfs.py' 'ac_private.py' 'verushash.py' 'paymentdisclosure.py' diff --git a/qa/rpc-tests/cryptoconditions.py b/qa/rpc-tests/cryptoconditions.py old mode 100755 new mode 100644 index ee119b75a..d5456e801 --- a/qa/rpc-tests/cryptoconditions.py +++ b/qa/rpc-tests/cryptoconditions.py @@ -3,7 +3,6 @@ # 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, \ @@ -25,7 +24,6 @@ def generate_random_string(length): random_string = ''.join(choice(ascii_uppercase) for i in range(length)) return random_string - class CryptoConditionsTest (BitcoinTestFramework): def setup_chain(self): @@ -105,13 +103,13 @@ class CryptoConditionsTest (BitcoinTestFramework): 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']: + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: 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']: + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: assert_equal(result[x][0], 'R') # no funds in the faucet yet @@ -179,12 +177,12 @@ class CryptoConditionsTest (BitcoinTestFramework): dice = rpc.diceaddress() assert_equal(dice['result'], 'success') - for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress']: + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: assert_equal(dice[x][0], 'R') dice = rpc.diceaddress(self.pubkey) assert_equal(dice['result'], 'success') - for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress', 'CCaddress']: + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: assert_equal(dice[x][0], 'R') # no dice created yet @@ -333,12 +331,12 @@ class CryptoConditionsTest (BitcoinTestFramework): rpc = self.nodes[0] result = rpc.tokenaddress() assert_success(result) - for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress']: + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: assert_equal(result[x][0], 'R') result = rpc.tokenaddress(self.pubkey) assert_success(result) - for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress', 'CCaddress']: + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: assert_equal(result[x][0], 'R') # there are no tokens created yet result = rpc.tokenlist() @@ -361,17 +359,6 @@ class CryptoConditionsTest (BitcoinTestFramework): 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) @@ -421,7 +408,7 @@ class CryptoConditionsTest (BitcoinTestFramework): tokenask = rpc.tokenask("100", tokenid, "7.77") tokenaskhex = tokenask['hex'] tokenaskid = self.send_and_mine(tokenask['hex'], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) order = result[0] assert order, "found order" @@ -440,7 +427,7 @@ class CryptoConditionsTest (BitcoinTestFramework): assert txid, "found txid" # should be no token orders - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # checking ask cancellation @@ -448,7 +435,7 @@ class CryptoConditionsTest (BitcoinTestFramework): testorderid = self.send_and_mine(testorder['hex'], rpc) cancel = rpc.tokencancelask(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # invalid numtokens bid @@ -474,7 +461,7 @@ class CryptoConditionsTest (BitcoinTestFramework): tokenbid = rpc.tokenbid("100", tokenid, "10") tokenbidhex = tokenbid['hex'] tokenbidid = self.send_and_mine(tokenbid['hex'], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) order = result[0] assert order, "found order" @@ -493,7 +480,7 @@ class CryptoConditionsTest (BitcoinTestFramework): assert txid, "found txid" # should be no token orders - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # checking bid cancellation @@ -501,7 +488,7 @@ class CryptoConditionsTest (BitcoinTestFramework): testorderid = self.send_and_mine(testorder['hex'], rpc) cancel = rpc.tokencancelbid(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # invalid token transfer amount (have to add status to CC code!) @@ -522,11 +509,11 @@ class CryptoConditionsTest (BitcoinTestFramework): def run_rewards_tests(self): rpc = self.nodes[0] result = rpc.rewardsaddress() - for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress']: + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: assert_equal(result[x][0], 'R') result = rpc.rewardsaddress(self.pubkey) - for x in ['RewardsCCaddress', 'myCCaddress', 'Rewardsmarker', 'myaddress', 'CCaddress']: + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: assert_equal(result[x][0], 'R') # no rewards yet @@ -637,12 +624,13 @@ class CryptoConditionsTest (BitcoinTestFramework): result = rpc.oraclesaddress() assert_success(result) - for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: assert_equal(result[x][0], 'R') result = rpc.oraclesaddress(self.pubkey) assert_success(result) - for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: assert_equal(result[x][0], 'R') # there are no oracles created yet @@ -674,7 +662,6 @@ class CryptoConditionsTest (BitcoinTestFramework): # assert_success(result) # globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex'], rpc) - def run_test (self): print("Mining blocks...") rpc = self.nodes[0] diff --git a/qa/rpc-tests/cryptoconditions_channels.py b/qa/rpc-tests/cryptoconditions_channels.py index b2a49b477..7f82f2f3c 100755 --- a/qa/rpc-tests/cryptoconditions_channels.py +++ b/qa/rpc-tests/cryptoconditions_channels.py @@ -26,6 +26,16 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): rpc = self.nodes[0] rpc1 = self.nodes[1] + # checking channelsaddress call + + result = rpc.channelsaddress(self.pubkey) + assert_success(result) + # test that additional CCaddress key is returned + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') + # getting empty channels list result = rpc.channelslist() assert_equal(len(result), 2) @@ -71,7 +81,7 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): # now in channelinfo payment information should appear result = rpc.channelsinfo(channel_txid) assert_equal(result["Transactions"][1]["Payment"], payment_tx_id) - + # number of payments should be equal 1 (one denomination used) result = rpc.channelsinfo(channel_txid)["Transactions"][1]["Number of payments"] assert_equal(result, 1) @@ -143,7 +153,7 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework): refund_txid = self.send_and_mine(result["hex"], rpc) assert refund_txid, "got txid" - # TODO: check if it refunded to opener address + # checking if it refunded to opener address raw_transaction = rpc.getrawtransaction(refund_txid, 1) result = raw_transaction["vout"][2]["valueSat"] diff --git a/qa/rpc-tests/cryptoconditions_dice.py b/qa/rpc-tests/cryptoconditions_dice.py index 51fd18908..7b9d3fbae 100755 --- a/qa/rpc-tests/cryptoconditions_dice.py +++ b/qa/rpc-tests/cryptoconditions_dice.py @@ -24,15 +24,21 @@ class CryptoconditionsDiceTest(CryptoconditionsTestFramework): 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') + result = rpc.diceaddress() + assert_equal(result['result'], 'success') - dice = rpc.diceaddress(self.pubkey) - assert_equal(dice['result'], 'success') - for x in ['myCCaddress', 'DiceCCaddress', 'Dicemarker', 'myaddress', 'CCaddress']: - assert_equal(dice[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') + + result = rpc.diceaddress(self.pubkey) + for x in result.keys(): + print(x+": "+str(result[x])) + assert_equal(result['result'], 'success') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # no dice created yet result = rpc.dicelist() diff --git a/qa/rpc-tests/cryptoconditions_faucet.py b/qa/rpc-tests/cryptoconditions_faucet.py index a3cbdeb15..27c5fce4e 100755 --- a/qa/rpc-tests/cryptoconditions_faucet.py +++ b/qa/rpc-tests/cryptoconditions_faucet.py @@ -24,17 +24,23 @@ class CryptoconditionsFaucetTest(CryptoconditionsTestFramework): assert_greater_than(result['balance'], 0.0) balance = result['balance'] - faucet = rpc.faucetaddress() - assert_equal(faucet['result'], 'success') + result = rpc.faucetaddress() + assert_equal(result['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') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.faucetaddress(self.pubkey) assert_success(result) + for x in result.keys(): + print(x+": "+str(result[x])) # test that additional CCaddress key is returned - for x in ['myCCaddress', 'FaucetCCaddress', 'Faucetmarker', 'myaddress', 'CCaddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # no funds in the faucet yet result = rpc.faucetget() diff --git a/qa/rpc-tests/cryptoconditions_gateways.py b/qa/rpc-tests/cryptoconditions_gateways.py index a7f0cad2b..b12ea9f7e 100755 --- a/qa/rpc-tests/cryptoconditions_gateways.py +++ b/qa/rpc-tests/cryptoconditions_gateways.py @@ -20,8 +20,10 @@ class CryptoconditionsGatewaysTest(CryptoconditionsTestFramework): result = rpc.gatewaysaddress() assert_success(result) - for x in ['GatewaysCCaddress', 'myCCaddress', 'Gatewaysmarker', 'myaddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') assert_equal("03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40", result["GatewaysPubkey"]) diff --git a/qa/rpc-tests/cryptoconditions_heir.py b/qa/rpc-tests/cryptoconditions_heir.py index 95b90b397..12ca8b3da 100755 --- a/qa/rpc-tests/cryptoconditions_heir.py +++ b/qa/rpc-tests/cryptoconditions_heir.py @@ -20,35 +20,36 @@ class CryptoconditionsHeirTest(CryptoconditionsTestFramework): rpc = self.nodes[0] rpc1 = self.nodes[1] - result = rpc.heiraddress() + result = rpc.heiraddress('') assert_success(result) + # verify all keys look like valid AC addrs, could be better - for x in ['myCCaddress', 'HeirCCaddress', 'Heirmarker', 'myaddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.heiraddress(self.pubkey) assert_success(result) + # test that additional CCaddress key is returned - for x in ['myCCaddress', 'HeirCCaddress', 'Heirmarker', 'myaddress', 'CCaddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # getting empty heir list result = rpc.heirlist() - assert_equal(len(result), 1) - assert_success(result) + assert_equal(result, []) # valid heirfund case with coins - result = rpc.heirfund("0", "1000", "UNITHEIR", self.pubkey1, "10") + result = rpc.heirfund("0", "1000", "UNITHEIR", self.pubkey1, "10", "TESTMEMO") assert_success(result) - heir_fund_txid = self.send_and_mine(result["hextx"], rpc) + heir_fund_txid = self.send_and_mine(result["hex"], rpc) assert heir_fund_txid, "got heir funding txid" # heir fund txid should be in heirlist now result = rpc.heirlist() - assert_equal(len(result), 2) - assert_success(result) - assert_equal(result["fundingtxid"], heir_fund_txid) + assert_equal(result, [heir_fund_txid]) # checking heirinfo result = rpc.heirinfo(heir_fund_txid) @@ -57,20 +58,20 @@ class CryptoconditionsHeirTest(CryptoconditionsTestFramework): assert_equal(result["name"], "UNITHEIR") assert_equal(result["owner"], self.pubkey) assert_equal(result["heir"], self.pubkey1) - assert_equal(result["funding total in coins"], "1000.00000000") - assert_equal(result["funding available in coins"], "1000.00000000") - assert_equal(result["inactivity time setting, sec"], "10") - assert_equal(result["spending allowed for the heir"], "false") - - # TODO: heirlist keys are duplicating now + assert_equal(result["memo"], "TESTMEMO") + assert_equal(result["lifetime"], "1000.00000000") + assert_equal(result["type"], "coins") + assert_equal(result["InactivityTimeSetting"], "10") + assert_equal(result["InactivityTime"], "0") + assert_equal(result["IsHeirSpendingAllowed"], "false") # waiting for 11 seconds to be sure that needed time passed for heir claiming time.sleep(11) rpc.generate(1) self.sync_all() result = rpc.heirinfo(heir_fund_txid) - assert_equal(result["funding available in coins"], "1000.00000000") - assert_equal(result["spending allowed for the heir"], "true") + assert_equal(result["lifetime"], "1000.00000000") + assert_equal(result["IsHeirSpendingAllowed"], "true") # have to check that second node have coins to cover txfee at least rpc.sendtoaddress(rpc1.getnewaddress(), 1) @@ -84,7 +85,7 @@ class CryptoconditionsHeirTest(CryptoconditionsTestFramework): result = rpc1.heirclaim("0", "1000", heir_fund_txid) assert_success(result) - heir_claim_txid = self.send_and_mine(result["hextx"], rpc1) + heir_claim_txid = self.send_and_mine(result["hex"], rpc1) assert heir_claim_txid, "got claim txid" # balance of second node after heirclaim should increase for 1000 coins - txfees @@ -96,9 +97,63 @@ class CryptoconditionsHeirTest(CryptoconditionsTestFramework): # no more funds should be available for claiming result = rpc.heirinfo(heir_fund_txid) - assert_equal(result["funding available in coins"], "0.00000000") + assert_equal(result["lifetime"], "1000.00000000") + assert_equal(result["available"], "0.00000000") - # TODO: valid heirfund case with tokens + # creating tokens which we put to heir contract + token_hex = rpc.tokencreate("TEST", "1", "TESTING") + token_txid = self.send_and_mine(token_hex["hex"], rpc) + assert token_txid, "got token txid" + + # checking possesion over the tokens and balance + result = rpc.tokenbalance(token_txid, self.pubkey)["balance"] + assert_equal(result, 100000000) + + # valid heir case with tokens + token_heir_hex = rpc.heirfund("0", "100000000", "UNITHEIR", self.pubkey1, "10", "TESTMEMO", token_txid) + token_heir_txid = self.send_and_mine(token_heir_hex["hex"], rpc) + assert token_heir_txid, "got txid of heirfund with tokens" + + self.sync_all() + + # checking heirinfo + result = rpc.heirinfo(token_heir_txid) + assert_success(result) + assert_equal(result["fundingtxid"], token_heir_txid) + assert_equal(result["name"], "UNITHEIR") + assert_equal(result["owner"], self.pubkey) + assert_equal(result["heir"], self.pubkey1) + assert_equal(result["lifetime"], "100000000") + assert_equal(result["type"], "tokens") + assert_equal(result["InactivityTimeSetting"], "10") + assert_equal(result["InactivityTime"], "0") + assert_equal(result["IsHeirSpendingAllowed"], "false") + + # waiting for 11 seconds to be sure that needed time passed for heir claiming + time.sleep(11) + rpc.generate(1) + self.sync_all() + result = rpc.heirinfo(token_heir_txid) + assert_equal(result["lifetime"], "100000000") + assert_equal(result["IsHeirSpendingAllowed"], "true") + + # let's claim whole heir sum from second node + result = rpc1.heirclaim("0", "100000000", token_heir_txid) + assert_success(result) + + heir_tokens_claim_txid = self.send_and_mine(result["hex"], rpc1) + assert heir_tokens_claim_txid, "got claim txid" + + # claiming node should have correct token balance now + result = rpc1.tokenbalance(token_txid, self.pubkey1)["balance"] + assert_equal(result, 100000000) + + self.sync_all() + + # no more funds should be available for claiming + result = rpc.heirinfo(token_heir_txid) + assert_equal(result["lifetime"], "100000000") + assert_equal(result["available"], "0") def run_test(self): print("Mining blocks...") diff --git a/qa/rpc-tests/cryptoconditions_oracles.py b/qa/rpc-tests/cryptoconditions_oracles.py index 048b577d1..953df5ca9 100755 --- a/qa/rpc-tests/cryptoconditions_oracles.py +++ b/qa/rpc-tests/cryptoconditions_oracles.py @@ -22,13 +22,17 @@ class CryptoconditionsOraclesTest(CryptoconditionsTestFramework): result = rpc.oraclesaddress() assert_success(result) - for x in ['OraclesCCaddress', 'Oraclesmarker', 'myCCaddress', 'myaddress']: - assert_equal(result[x][0], 'R') + + for x in result.keys(): + if x.find('ddress') > 0: + 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') + + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # there are no oracles created yet result = rpc.oracleslist() diff --git a/qa/rpc-tests/cryptoconditions_rewards.py b/qa/rpc-tests/cryptoconditions_rewards.py index 7bda54eaf..57d3032b8 100755 --- a/qa/rpc-tests/cryptoconditions_rewards.py +++ b/qa/rpc-tests/cryptoconditions_rewards.py @@ -15,14 +15,18 @@ from cryptoconditions import assert_success, assert_error, generate_random_strin class CryptoconditionsRewardsTest(CryptoconditionsTestFramework): 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') + for x in result.keys(): + if x.find('ddress') > 0: + 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') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # no rewards yet result = rpc.rewardslist() diff --git a/qa/rpc-tests/cryptoconditions_token.py b/qa/rpc-tests/cryptoconditions_token.py index 97ed86f8d..263d85dde 100755 --- a/qa/rpc-tests/cryptoconditions_token.py +++ b/qa/rpc-tests/cryptoconditions_token.py @@ -21,23 +21,27 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): result = rpc.tokenaddress() assert_success(result) - for x in ['TokensCCaddress', 'myCCaddress', 'Tokensmarker', 'myaddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.tokenaddress(self.pubkey) assert_success(result) - for x in ['TokensCCaddress', 'myCCaddress', 'Tokensmarker', 'myaddress', 'CCaddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.assetsaddress() assert_success(result) - for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') result = rpc.assetsaddress(self.pubkey) assert_success(result) - for x in ['AssetsCCaddress', 'myCCaddress', 'Assetsmarker', 'myaddress', 'CCaddress']: - assert_equal(result[x][0], 'R') + for x in result.keys(): + if x.find('ddress') > 0: + assert_equal(result[x][0], 'R') # there are no tokens created yet result = rpc.tokenlist() @@ -61,7 +65,7 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): assert_equal(result[0], tokenid) # there are no token orders yet - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # getting token balance for non existing tokenid @@ -117,7 +121,7 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): tokenask = rpc.tokenask("100", tokenid, "7.77") tokenaskhex = tokenask['hex'] tokenaskid = self.send_and_mine(tokenask['hex'], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) order = result[0] assert order, "found order" @@ -136,7 +140,7 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): assert txid, "found txid" # should be no token orders - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # checking ask cancellation @@ -157,7 +161,7 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): # from valid node cancel = rpc.tokencancelask(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) @@ -184,7 +188,7 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): tokenbid = rpc.tokenbid("100", tokenid, "10") tokenbidhex = tokenbid['hex'] tokenbidid = self.send_and_mine(tokenbid['hex'], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) order = result[0] assert order, "found order" @@ -203,7 +207,7 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): assert txid, "found txid" # should be no token orders - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # checking bid cancellation @@ -220,7 +224,7 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework): # from valid node cancel = rpc.tokencancelbid(tokenid, testorderid) self.send_and_mine(cancel["hex"], rpc) - result = rpc.tokenorders() + result = rpc.tokenorders(tokenid) assert_equal(result, []) # invalid token transfer amount (have to add status to CC code!) diff --git a/qa/rpc-tests/dpowconfs.py b/qa/rpc-tests/dpowconfs.py new file mode 100755 index 000000000..9c449923e --- /dev/null +++ b/qa/rpc-tests/dpowconfs.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 The Hush developers +# Copyright (c) 2019 The 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.util import * +import time + +class DPoWConfsTest(BitcoinTestFramework): + def debug_info(self): + rpc = self.nodes[0] + print "-- DEBUG --" + getinfo = rpc.getinfo() + getwalletinfo = rpc.getwalletinfo() + listreceivedbyaddress = rpc.listreceivedbyaddress() + print "notarized=", getinfo['notarized'], " blocks=", getinfo['blocks'] + #print "getinfo=", getinfo + print "balance=", getwalletinfo['balance'] + #print "getwalletinfo=", getwalletinfo + print "listreceivedbyaddress=", listreceivedbyaddress + print "-- DEBUG --" + + def setup_chain(self): + self.num_nodes = 1 + print("Initializing DPoWconfs test directory "+self.options.tmpdir) + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self): + print("Setting up network...") + self.nodes = [] + self.is_network_split = False + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.nodes = start_nodes( self.num_nodes, self.options.tmpdir, + extra_args=[[ + '-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' + ]] + ) + self.sync_all() + + def run_test(self): + rpc = self.nodes[0] + # 98 is notarized, next will be 105. Must mine at least 101 + # blocks for 100 block maturity rule + blockhashes = rpc.generate(101) + # block 98, this is 0 indexed + notarizedhash = blockhashes[97] + self.debug_info() + + taddr = rpc.getnewaddress() + txid = rpc.sendtoaddress(taddr, 5.55) + # blocks 102,103 + rpc.generate(2) + self.debug_info() + + info = rpc.getinfo() + print "notarizedhash=", notarizedhash, "\n" + print "info[notarizedhash]", info['notarizedhash'], "\n" + assert_equal( info['notarizedhash'], notarizedhash) + + result = rpc.listunspent() + + # this xtn has 2 raw confs, but not in a notarized block, + # so dpowconfs holds it at 1 + for res in result: + if (res['address'] == taddr and res['generated'] == 'false'): + assert_equal( result[0]['confirmations'], 1 ) + assert_equal( result[0]['rawconfirmations'], 2 ) + + # we will now have 3 rawconfs but confirmations=1 because not notarized + # block 104 + rpc.generate(1) + self.debug_info() + minconf = 2 + result = rpc.listreceivedbyaddress(minconf) + print "listreceivedbyaddress(2)=", result, "\n" + + # nothing is notarized, so we should see no results for minconf=2 + assert len(result) == 0 + + print "getreceivedaddress" + received = rpc.getreceivedbyaddress(taddr, minconf) + assert_equal( received, 0.00000000) + + #received = rpc.getreceivedbyaddress(taddr) + #assert_equal( received, "5.55000000") + taddr = rpc.getnewaddress() + zaddr = rpc.z_getnewaddress() + # should get insufficient funds error + recipients = [ { "amount" : Decimal('4.20'), "address" : zaddr } ] + txid = rpc.z_sendmany( taddr, recipients, minconf) + + # generate a notarized block, block 105 and block 106 + # only generating the notarized block seems to have + # race conditions about whether the block is notarized + txids = rpc.generate(2) + self.debug_info() + + getinfo = rpc.getinfo() + # try to allow notarization data to update + print "Sleeping" + while (getinfo['blocks'] != 106) or (getinfo['notarized'] != 105): + printf(".") + time.sleep(1) + getinfo = rpc.getinfo() + + # make sure this block was notarized as we expect + #assert_equal(getinfo['blocks'], getinfo['notarized']) + #assert_equal(getinfo['notarizedhash'], txids[0]) + + result = rpc.listreceivedbyaddress(minconf) + print "listreceivedbyaddress(2)=", result + + assert_equal( len(result), 1, 'got one xtn with minconf=2' ) + + # verify we see the correct dpowconfs + rawconfs + assert_greater_than( result[0]['confirmations'], 1) + assert_greater_than( result[0]['rawconfirmations'], 1) + + print "listtransactions" + xtns = rpc.listtransactions() + # verify this rpc agrees with listreceivedbyaddress + assert_greater_than(xtns[0]['confirmations'], 1) + assert_greater_than(xtns[0]['rawconfirmations'], 1) + + print "getreceivedaddress" + received = rpc.getreceivedbyaddress(taddr, minconf) + assert_equal( "%.8f" % received, "5.55000000") + + received = rpc.getreceivedbyaddress(taddr) + assert_equal( "%.8f" % received, "5.55000000") + +if __name__ == '__main__': + DPoWConfsTest().main() diff --git a/qa/rpc-tests/test_framework/util.py b/qa/rpc-tests/test_framework/util.py index e9130171b..6bcf4cd18 100644 --- a/qa/rpc-tests/test_framework/util.py +++ b/qa/rpc-tests/test_framework/util.py @@ -1,9 +1,7 @@ # Copyright (c) 2014 The Bitcoin Core developers -# Copyright (c) 2018 The SuperNET developers +# Copyright (c) 2018-2019 The SuperNET developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - - # # Helpful routines for regression testing # @@ -415,10 +413,16 @@ def assert_true(condition, message = ""): def assert_false(condition, message = ""): assert_true(not condition, message) +# assert thing2 > thing1 def assert_greater_than(thing1, thing2): if thing1 <= thing2: raise AssertionError("%s <= %s"%(str(thing1),str(thing2))) +# assert thing2 >= thing1 +def assert_greater_than_or_equal(thing1, thing2): + if thing1 < thing2: + raise AssertionError("%s < %s"%(str(thing1),str(thing2))) + def assert_raises(exc, fun, *args, **kwds): try: fun(*args, **kwds) diff --git a/src/Makefile.am b/src/Makefile.am index ad9842f09..248f9aa27 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -36,7 +36,8 @@ LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl endif if TARGET_DARWIN LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl -else +endif +if TARGET_LINUX LIBBITCOIN_SERVER=libbitcoin_server.a -lcurl endif @@ -203,6 +204,7 @@ BITCOIN_CORE_H = \ mruset.h \ net.h \ netbase.h \ + notaries_staked.h \ noui.h \ paymentdisclosure.h \ paymentdisclosuredb.h \ @@ -286,6 +288,7 @@ libbitcoin_server_a_SOURCES = \ bloom.cpp \ cc/eval.cpp \ cc/import.cpp \ + cc/importgateway.cpp \ cc/CCassetsCore.cpp \ cc/CCcustom.cpp \ cc/CCtx.cpp \ @@ -310,6 +313,7 @@ libbitcoin_server_a_SOURCES = \ chain.cpp \ checkpoints.cpp \ crosschain.cpp \ + crosschain_authority.cpp \ crypto/haraka.h \ crypto/haraka_portable.h \ crypto/verus_hash.h \ @@ -324,6 +328,7 @@ libbitcoin_server_a_SOURCES = \ metrics.h \ miner.cpp \ net.cpp \ + notaries_staked.cpp \ noui.cpp \ notarisationdb.cpp \ paymentdisclosure.cpp \ @@ -486,6 +491,8 @@ libbitcoin_common_a_SOURCES = \ script/sign.cpp \ script/standard.cpp \ transaction_builder.cpp \ + cc/CCtokensOpRet.cpp \ + cc/CCutilbits.cpp \ $(BITCOIN_CORE_H) \ $(LIBZCASH_H) @@ -571,8 +578,17 @@ komodod_LDADD += \ $(LIBBITCOIN_CRYPTO) \ $(LIBVERUS_CRYPTO) \ $(LIBVERUS_PORTABLE_CRYPTO) \ - $(LIBZCASH_LIBS) \ - libcc.so + $(LIBZCASH_LIBS) + +if TARGET_DARWIN +komodod_LDADD += libcc.dylib $(LIBSECP256K1) +endif +if TARGET_WINDOWS +komodod_LDADD += libcc.dll $(LIBSECP256K1) +endif +if TARGET_LINUX +komodod_LDADD += libcc.so $(LIBSECP256K1) +endif if ENABLE_PROTON komodod_LDADD += $(LIBBITCOIN_PROTON) $(PROTON_LIBS) diff --git a/src/ac/ilien b/src/ac/ilien new file mode 100755 index 000000000..53e571661 --- /dev/null +++ b/src/ac/ilien @@ -0,0 +1,3 @@ +#!/bin/bash +./komodo-cli -ac_name=ILN $1 $2 $3 $4 $5 $6 + diff --git a/src/ac/iln b/src/ac/iln new file mode 100755 index 000000000..f453c338e --- /dev/null +++ b/src/ac/iln @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=ILN $1 $2 $3 $4 $5 $6 diff --git a/src/ac/morty b/src/ac/morty new file mode 100755 index 000000000..4579324b7 --- /dev/null +++ b/src/ac/morty @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=MORTY $1 $2 $3 $4 $5 $6 diff --git a/src/ac/rick b/src/ac/rick new file mode 100755 index 000000000..b68bd56ab --- /dev/null +++ b/src/ac/rick @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=RICK $1 $2 $3 $4 $5 $6 diff --git a/src/ac/vote2019 b/src/ac/vote2019 new file mode 100755 index 000000000..029558808 --- /dev/null +++ b/src/ac/vote2019 @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=VOTE2019 $1 $2 $3 $4 $5 $6 diff --git a/src/assetchains.json b/src/assetchains.json index 2775f5db3..87173697b 100644 --- a/src/assetchains.json +++ b/src/assetchains.json @@ -64,13 +64,10 @@ "ac_name": "MESH", "ac_supply": "1000007" }, - { - "ac_name": "MNZ", - "ac_supply": "257142858" - }, { "ac_name": "AXO", - "ac_supply": "200000000" + "ac_supply": "200000000", + "ac_ccactivate": "130000" }, { "ac_name": "ETOMIC", @@ -182,7 +179,7 @@ "190.114.254.104" ] }, - { + { "ac_name": "DION", "ac_supply": "3900000000", "ac_reward": "22260000000", @@ -240,5 +237,31 @@ "217.182.129.38", "37.187.225.231" ] - } + }, + { + "ac_name": "ILN", + "ac_supply": "10000000000", + "ac_cc": "2", + "addnode": ["51.75.122.83"] + }, + { + "ac_name": "RICK", + "ac_supply": "90000000000", + "ac_reward": "100000000", + "ac_cc": "3", + "addnode": ["138.201.136.145"] + }, + { + "ac_name": "MORTY", + "ac_supply": "90000000000", + "ac_reward": "100000000", + "ac_cc": "3", + "addnode": ["138.201.136.145"] + }, +{ + "ac_name": "VOTE2019", + "ac_supply": "123651638", + "ac_public": "1", + "addnode": ["95.213.238.98"] + } ] diff --git a/src/assetchains.old b/src/assetchains.old index 41cbbdae1..8f0d763b5 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -20,8 +20,7 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=KV -ac_supply=1000000 -addnode=78.47.196.146 $1 & ./komodod -pubkey=$pubkey -ac_name=CEAL -ac_supply=366666666 -addnode=78.47.196.146 $1 & ./komodod -pubkey=$pubkey -ac_name=MESH -ac_supply=1000007 -addnode=78.47.196.146 $1 & -./komodod -pubkey=$pubkey -ac_name=MNZ -ac_supply=257142858 -addnode=51.15.138.138 $1 & -./komodod -pubkey=$pubkey -ac_name=AXO -ac_supply=200000000 -addnode=78.47.196.146 & +./komodod -pubkey=$pubkey -ac_name=AXO -ac_supply=200000000 -ac_ccactivate=130000 -addnode=78.47.196.146 & ./komodod -pubkey=$pubkey -ac_name=ETOMIC -ac_supply=100000000 -addnode=78.47.196.146 & ./komodod -pubkey=$pubkey -ac_name=BTCH -ac_supply=20998641 -addnode=78.47.196.146 & ./komodod -pubkey=$pubkey -ac_name=BEER -ac_supply=100000000 -addnode=78.47.196.146 & @@ -47,4 +46,7 @@ echo $pubkey ./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 & ./komodod -pubkey=$pubkey -ac_name=KSB -ac_supply=1000000000 -ac_end=1 -ac_public=1 -addnode=37.187.225.231 & ./komodod -pubkey=$pubkey -ac_name=OUR -ac_reward=1478310502 -ac_halving=525600 -ac_cc=42 -ac_supply=100000000 -ac_perc=77700 -ac_staked=93 -ac_pubkey=02652a3f3e00b3a1875a918314f0bac838d6dd189a346fa623f5efe9541ac0b98c -ac_public=1 -addnode=51.255.195.65 -addnode=217.182.129.38 -addnode=37.187.225.231 & - +./komodod -pubkey=$pubkey -ac_name=ILN -ac_supply=10000000000 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=51.75.122.83 & +./komodod -pubkey=$pubkey -ac_name=RICK -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -addnode=138.201.136.145 & +./komodod -pubkey=$pubkey -ac_name=MORTY -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -addnode=138.201.136.145 & +./komodod -pubkey=$pubkey -ac_name=VOTE2019 -ac_supply=123651638 -ac_public=1 -addnode=95.213.238.98 & diff --git a/src/base58.cpp b/src/base58.cpp index 9d10b7e6a..383666d82 100644 --- a/src/base58.cpp +++ b/src/base58.cpp @@ -296,13 +296,13 @@ CTxDestination CBitcoinAddress::Get() const return CNoDestination(); } -bool CBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type) const +bool CBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type, bool ccflag) const { if (!IsValid()) { return false; } else if (vchVersion == Params().Base58Prefix(CChainParams::PUBKEY_ADDRESS)) { memcpy(&hashBytes, &vchData[0], 20); - type = 1; + ccflag ? type = 3 : type = 1; return true; } else if (vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS)) { memcpy(&hashBytes, &vchData[0], 20); @@ -336,6 +336,84 @@ bool CBitcoinAddress::IsScript() const return IsValid() && vchVersion == Params().Base58Prefix(CChainParams::SCRIPT_ADDRESS); } +bool CCustomBitcoinAddress::Set(const CKeyID& id) +{ + SetData(base58Prefixes[0], &id, 20); + return true; +} + +bool CCustomBitcoinAddress::Set(const CPubKey& key) +{ + CKeyID id = key.GetID(); + SetData(base58Prefixes[0], &id, 20); + return true; +} + +bool CCustomBitcoinAddress::Set(const CScriptID& id) +{ + SetData(base58Prefixes[1], &id, 20); + return true; +} + +bool CCustomBitcoinAddress::Set(const CTxDestination& dest) +{ + return boost::apply_visitor(CBitcoinAddressVisitor(this), dest); +} + +bool CCustomBitcoinAddress::IsValid() const +{ + bool fCorrectSize = vchData.size() == 20; + bool fKnownVersion = vchVersion == base58Prefixes[0] || + vchVersion == base58Prefixes[1]; + return fCorrectSize && fKnownVersion; +} + +bool CCustomBitcoinAddress::GetKeyID(CKeyID& keyID) const +{ + if (!IsValid() || vchVersion != base58Prefixes[0]) + return false; + uint160 id; + memcpy(&id, &vchData[0], 20); + keyID = CKeyID(id); + return true; +} + +CTxDestination CCustomBitcoinAddress::Get() const +{ + if (!IsValid()) + return CNoDestination(); + uint160 id; + memcpy(&id, &vchData[0], 20); + if (vchVersion == base58Prefixes[0]) + return CKeyID(id); + else if (vchVersion == base58Prefixes[1]) + return CScriptID(id); + else + return CNoDestination(); +} + +bool CCustomBitcoinAddress::GetIndexKey(uint160& hashBytes, int& type, bool ccflag) const +{ + if (!IsValid()) { + return false; + } else if (vchVersion == base58Prefixes[0]) { + memcpy(&hashBytes, &vchData[0], 20); + ccflag ? type = 3 : type = 1; + return true; + } else if (vchVersion == base58Prefixes[1]) { + memcpy(&hashBytes, &vchData[0], 20); + type = 2; + return true; + } + + return false; +} + +bool CCustomBitcoinAddress::IsScript() const +{ + return IsValid() && vchVersion == base58Prefixes[1]; +} + void CBitcoinSecret::SetKey(const CKey& vchSecret) { assert(vchSecret.IsValid()); diff --git a/src/base58.h b/src/base58.h index 4decb4922..8be0247e0 100644 --- a/src/base58.h +++ b/src/base58.h @@ -130,9 +130,9 @@ public: */ class CBitcoinAddress : public CBase58Data { public: - bool Set(const CKeyID &id); - bool Set(const CPubKey &key); - bool Set(const CScriptID &id); + virtual bool Set(const CKeyID &id); + virtual bool Set(const CPubKey &key); + virtual bool Set(const CScriptID &id); bool Set(const CTxDestination &dest); bool IsValid() const; bool IsValid(const CChainParams ¶ms) const; @@ -147,7 +147,32 @@ public: CTxDestination Get() const; bool GetKeyID(CKeyID &keyID) const; bool GetKeyID_NoCheck(CKeyID& keyID) const; - bool GetIndexKey(uint160& hashBytes, int& type) const; + bool GetIndexKey(uint160& hashBytes, int& type, bool ccflag) const; + bool IsScript() const; +}; + +class CCustomBitcoinAddress : public CBitcoinAddress { + std::vector base58Prefixes[2]; +public: + bool Set(const CKeyID &id); + bool Set(const CPubKey &key); + bool Set(const CScriptID &id); + bool Set(const CTxDestination &dest); + bool IsValid() const; + + CCustomBitcoinAddress() {} + CCustomBitcoinAddress(const CTxDestination &dest,uint8_t taddr,uint8_t pubkey_prefix,uint8_t script_prefix) + { + if (taddr!=0) base58Prefixes[0].push_back(taddr); + base58Prefixes[0].push_back(pubkey_prefix); + base58Prefixes[1].push_back(script_prefix); + Set(dest); + } + + CTxDestination Get() const; + bool GetKeyID(CKeyID &keyID) const; + bool GetKeyID_NoCheck(CKeyID& keyID) const; + bool GetIndexKey(uint160& hashBytes, int& type, bool ccflag) const; bool IsScript() const; }; diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index b2fa534a1..68e0da8b3 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -59,27 +59,46 @@ static bool fDaemon; #include "komodo_defs.h" #define KOMODO_ASSETCHAIN_MAXLEN 65 extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern int32_t ASSETCHAINS_BLOCKTIME; +extern uint64_t ASSETCHAINS_CBOPRET; void komodo_passport_iteration(); uint64_t komodo_interestsum(); int32_t komodo_longestchain(); +void komodo_cbopretupdate(int32_t forceflag); void WaitForShutdown(boost::thread_group* threadGroup) { - bool fShutdown = ShutdownRequested(); + int32_t i; bool fShutdown = ShutdownRequested(); // Tell the main threads to shutdown. + if ( ASSETCHAINS_CBOPRET != 0 ) + komodo_pricesinit(); while (!fShutdown) { //fprintf(stderr,"call passport iteration\n"); if ( ASSETCHAINS_SYMBOL[0] == 0 ) { komodo_passport_iteration(); - MilliSleep(10000); + for (i=0; i<10; i++) + { + fShutdown = ShutdownRequested(); + if ( fShutdown != 0 ) + break; + MilliSleep(1000); + } } else { //komodo_interestsum(); //komodo_longestchain(); - MilliSleep(20000); + if ( ASSETCHAINS_CBOPRET != 0 ) + komodo_cbopretupdate(0); + for (i=0; i<=ASSETCHAINS_BLOCKTIME/5; i++) + { + fShutdown = ShutdownRequested(); + if ( fShutdown != 0 ) + break; + MilliSleep(1000); + } } fShutdown = ShutdownRequested(); } diff --git a/src/cc/CCGateways.h b/src/cc/CCGateways.h index b629ade1f..8dfed186f 100644 --- a/src/cc/CCGateways.h +++ b/src/cc/CCGateways.h @@ -18,23 +18,23 @@ #define CC_GATEWAYS_H #include "CCinclude.h" -#include "../merkleblock.h" bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); -std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys); +std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4); std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount); std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount); std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount); std::string GatewaysPartialSign(uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex); std::string GatewaysCompleteSigning(uint64_t txfee,uint256 txidaddr,std::string refcoin,std::string hex); std::string GatewaysMarkDone(uint64_t txfee,uint256 withdrawtxid,std::string refcoin); +UniValue GatewaysPendingDeposits(uint256 bindtxid,std::string refcoin); UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin); UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin); -UniValue GatewaysMultisig(char *txidaddr); - // CCcustom UniValue GatewaysInfo(uint256 bindtxid); +UniValue GatewaysExternalAddress(uint256 bindtxid,CPubKey pubkey); +UniValue GatewaysDumpPrivKey(uint256 bindtxid,CKey privkey); UniValue GatewaysList(); #endif diff --git a/src/cc/CCHeir.h b/src/cc/CCHeir.h index 30334f6e1..fa1f72263 100644 --- a/src/cc/CCHeir.h +++ b/src/cc/CCHeir.h @@ -27,15 +27,11 @@ bool HeirValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, class CoinHelper; class TokenHelper; -UniValue HeirFundCoinCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); -UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid); +UniValue HeirFundCoinCaller(int64_t txfee, int64_t coins, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string memo); +UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string memo, uint256 tokenid); UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string amount); UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string amount); - UniValue HeirInfo(uint256 fundingtxid); UniValue HeirList(); -//std::string Heir_MakeBadTx(uint256 fundingtxid, uint8_t funcId, int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTime, uint32_t errMask); - -//bool HeirExactTokenAmounts(bool compareTotals, struct CCcontract_info *cpHeir, Eval* eval, uint256 assetid, const CTransaction &tx); #endif diff --git a/src/cc/CCImportGateway.h b/src/cc/CCImportGateway.h new file mode 100644 index 000000000..995cde460 --- /dev/null +++ b/src/cc/CCImportGateway.h @@ -0,0 +1,38 @@ +/****************************************************************************** + * Copyright © 2014-2018 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + + +#ifndef CC_IMPORTGATEWAY_H +#define CC_IMPORTGATEWAY_H + +#include "CCinclude.h" + +// CCcustom +bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn); +bool ImportGatewayExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); +std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4); +std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t burnvout,std::string rawburntx,std::vectorproof,CPubKey destpub); +std::string ImportGatewayWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount); +std::string ImportGatewayPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex); +std::string ImportGatewayCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex); +std::string ImportGatewayMarkDone(uint64_t txfee,uint256 completetxid,std::string refcoin); +UniValue ImportGatewayPendingDeposits(uint256 bindtxid,std::string refcoin); +UniValue ImportGatewayPendingWithdraws(uint256 bindtxid,std::string refcoin); +UniValue ImportGatewayProcessedWithdraws(uint256 bindtxid,std::string refcoin); +UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey); +UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key); +UniValue ImportGatewayList(); +UniValue ImportGatewayInfo(uint256 bindtxid); +#endif \ No newline at end of file diff --git a/src/cc/CCMarmara.h b/src/cc/CCMarmara.h index 65269df77..85f9175d1 100644 --- a/src/cc/CCMarmara.h +++ b/src/cc/CCMarmara.h @@ -14,8 +14,8 @@ ******************************************************************************/ -#ifndef CC_TRIGGERS_H -#define CC_TRIGGERS_H +#ifndef CC_MARMARA_H +#define CC_MARMARA_H #include "CCinclude.h" #include "../komodo_cJSON.h" @@ -24,6 +24,7 @@ #define MARMARA_MINLOCK (1440 * 3 * 30) #define MARMARA_MAXLOCK (1440 * 24 * 30) #define MARMARA_VINS 16 +#define EVAL_MARMARA 0xef extern uint8_t ASSETCHAINS_MARMARA; uint64_t komodo_block_prg(uint32_t nHeight); diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 187d9c9ad..247a0f2ec 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -19,9 +19,16 @@ #include "CCinclude.h" +#define PAYMENTS_TXFEE 10000 + bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PaymentsInfo(); +UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr); #endif diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 1a68be7dd..633cc32e2 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -18,17 +18,34 @@ #define CC_PRICES_H #include "CCinclude.h" +int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind); +int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); + +#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +#define PRICES_TXFEE 10000 +#define PRICES_MAXLEVERAGE 777 +#define PRICES_SMOOTHWIDTH 1 +#define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 +#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) +#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) +#define PRICES_MULT (KOMODO_MAXPRICES * 2) +#define PRICES_DIV (KOMODO_MAXPRICES * 3) +#define PRICES_INV (KOMODO_MAXPRICES * 4) +#define PRICES_MDD (KOMODO_MAXPRICES * 5) +#define PRICES_MMD (KOMODO_MAXPRICES * 6) +#define PRICES_MMM (KOMODO_MAXPRICES * 7) +#define PRICES_DDD (KOMODO_MAXPRICES * 8) bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom +UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); +UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount); +UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid); +UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight); +UniValue PricesCashout(uint64_t txfee,uint256 bettxid); +UniValue PricesInfo(uint256 bettxid,int32_t refheight); UniValue PricesList(); -UniValue PricesInfo(uint256 fundingtxid); -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid); -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys); -std::string PricesAddFunding(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount); -std::string PricesBet(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount,int32_t leverage); -std::string PricesFinish(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,uint256 bettxid); #endif diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index 69fcfacbb..7b31c094d 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -29,11 +29,8 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCassetsCore -//CTxOut MakeAssetsVout(CAmount nValue,CPubKey pk); -//CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description); -//CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 tokenid, uint256 assetid2, int64_t price, std::vector origpubkey); -//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description); -//uint8_t DecodeAssetOpRet(const CScript &scriptPubKey, uint8_t &evalCode, uint256 &assetid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); +vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey); +uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CTransaction &tx); int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid); bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice); @@ -50,7 +47,7 @@ bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t //int64_t GetAssetBalance(CPubKey pk,uint256 tokenid); // --> GetTokenBalance() int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs); -UniValue AssetOrders(uint256 tokenid); +UniValue AssetOrders(uint256 tokenid, CPubKey pubkey, uint8_t additionalEvalCode); //UniValue AssetInfo(uint256 tokenid); //UniValue AssetList(); //std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description); diff --git a/src/cc/CCassetsCore.cpp b/src/cc/CCassetsCore.cpp index 1b8e46189..06d843b40 100644 --- a/src/cc/CCassetsCore.cpp +++ b/src/cc/CCassetsCore.cpp @@ -43,17 +43,17 @@ bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64 int64_t unitprice,recvunitprice,newunitprice=0; if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); return(false); } else if ( totalunits != (remaining_units + paidunits) ) { - fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits); + fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_units %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_units + paidunits),(long long)remaining_units,(long long)paidunits); return(false); } else if ( orig_nValue != (remaining_nValue + received_nValue) ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); return(false); } else @@ -68,10 +68,10 @@ bool ValidateBidRemainder(int64_t remaining_units,int64_t remaining_nValue,int64 newunitprice = (remaining_nValue / remaining_units); if ( recvunitprice < unitprice ) { - fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); + fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); return(false); } - fprintf(stderr,"orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); + fprintf(stderr,"ValidateAssetRemainder() orig %llu total %llu, recv %llu paid %llu,recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(long long)orig_nValue,(long long)totalunits,(long long)received_nValue,(long long)paidunits,(double)recvunitprice/(COIN),(double)unitprice/(COIN),(double)newunitprice/(COIN)); } return(true); } @@ -89,7 +89,7 @@ bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t paidunits = totalunits; received_nValue = orig_nValue; remaining_units = 0; - fprintf(stderr,"totally filled!\n"); + fprintf(stderr,"SetBidFillamounts() bid order totally filled!\n"); return(true); } remaining_units = (totalunits - paidunits); @@ -100,7 +100,7 @@ bool SetBidFillamounts(int64_t &received_nValue,int64_t &remaining_units,int64_t if ( unitprice > 0 && received_nValue > 0 && received_nValue <= orig_nValue ) { remaining_nValue = (orig_nValue - received_nValue); - printf("total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); + printf("SetBidFillamounts() total.%llu - paid.%llu, remaining %llu <- %llu (%llu - %llu)\n",(long long)totalunits,(long long)paidunits,(long long)remaining_nValue,(long long)(orig_nValue - received_nValue),(long long)orig_nValue,(long long)received_nValue); return(ValidateBidRemainder(remaining_units,remaining_nValue,orig_nValue,received_nValue,paidunits,totalunits)); } else return(false); } @@ -118,14 +118,14 @@ bool SetAskFillamounts(int64_t &received_assetoshis,int64_t &remaining_nValue,in paid_nValue = total_nValue; received_assetoshis = orig_assetoshis; remaining_nValue = 0; - fprintf(stderr,"totally filled!\n"); + fprintf(stderr,"SetAskFillamounts() ask order totally filled!\n"); return(true); } remaining_nValue = (total_nValue - paid_nValue); dunitprice = ((double)total_nValue / orig_assetoshis); received_assetoshis = (paid_nValue / dunitprice); - fprintf(stderr,"remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN); - fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); + fprintf(stderr,"SetAskFillamounts() remaining_nValue %.8f (%.8f - %.8f)\n",(double)remaining_nValue/COIN,(double)total_nValue/COIN,(double)paid_nValue/COIN); + fprintf(stderr,"SetAskFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) { remaining_assetoshis = (orig_assetoshis - received_assetoshis); @@ -138,17 +138,17 @@ bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis, int64_t unitprice,recvunitprice,newunitprice=0; if ( orig_assetoshis == 0 || received_assetoshis == 0 || paid_nValue == 0 || total_nValue == 0 ) { - fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis == %llu || received_assetoshis == %llu || paid_nValue == %llu || total_nValue == %llu\n",(long long)orig_assetoshis,(long long)received_assetoshis,(long long)paid_nValue,(long long)total_nValue); return(false); } else if ( total_nValue != (remaining_nValue + paid_nValue) ) { - fprintf(stderr,"ValidateAssetRemainder: total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue); + fprintf(stderr,"ValidateAssetRemainder() total_nValue %llu != %llu (remaining_nValue %llu + %llu paid_nValue)\n",(long long)total_nValue,(long long)(remaining_nValue + paid_nValue),(long long)remaining_nValue,(long long)paid_nValue); return(false); } else if ( orig_assetoshis != (remaining_assetoshis + received_assetoshis) ) { - fprintf(stderr,"ValidateAssetRemainder: orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis); + fprintf(stderr,"ValidateAssetRemainder() orig_assetoshis %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_assetoshis,(long long)(remaining_assetoshis - received_assetoshis),(long long)remaining_assetoshis,(long long)received_assetoshis); return(false); } else @@ -159,10 +159,10 @@ bool ValidateAskRemainder(int64_t remaining_nValue,int64_t remaining_assetoshis, newunitprice = (remaining_nValue / remaining_assetoshis); if ( recvunitprice < unitprice ) { - fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); + fprintf(stderr,"ValidateAskRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); return(false); } - fprintf(stderr,"got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); + fprintf(stderr,"ValidateAskRemainder() got recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/COIN,(double)unitprice/COIN,(double)newunitprice/COIN); } return(true); } @@ -172,7 +172,7 @@ bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetosh int64_t remaining_assetoshis; double dunitprice; if ( total_assetoshis2 == 0 ) { - fprintf(stderr,"total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2); + fprintf(stderr,"SetSwapFillamounts() total_assetoshis2.0 origsatoshis.%llu paid_assetoshis2.%llu\n",(long long)orig_assetoshis,(long long)paid_assetoshis2); received_assetoshis = remaining_assetoshis2 = paid_assetoshis2 = 0; return(false); } @@ -181,14 +181,14 @@ bool SetSwapFillamounts(int64_t &received_assetoshis,int64_t &remaining_assetosh paid_assetoshis2 = total_assetoshis2; received_assetoshis = orig_assetoshis; remaining_assetoshis2 = 0; - fprintf(stderr,"totally filled!\n"); + fprintf(stderr,"SetSwapFillamounts() swap order totally filled!\n"); return(true); } remaining_assetoshis2 = (total_assetoshis2 - paid_assetoshis2); dunitprice = ((double)total_assetoshis2 / orig_assetoshis); received_assetoshis = (paid_assetoshis2 / dunitprice); - fprintf(stderr,"remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN); - fprintf(stderr,"unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); + fprintf(stderr,"SetSwapFillamounts() remaining_assetoshis2 %llu (%llu - %llu)\n",(long long)remaining_assetoshis2/COIN,(long long)total_assetoshis2/COIN,(long long)paid_assetoshis2/COIN); + fprintf(stderr,"SetSwapFillamounts() unitprice %.8f received_assetoshis %llu orig %llu\n",dunitprice/COIN,(long long)received_assetoshis,(long long)orig_assetoshis); if ( fabs(dunitprice) > SMALLVAL && received_assetoshis > 0 && received_assetoshis <= orig_assetoshis ) { remaining_assetoshis = (orig_assetoshis - received_assetoshis); @@ -201,17 +201,17 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6 int64_t unitprice,recvunitprice,newunitprice=0; if ( orig_nValue == 0 || received_nValue == 0 || paidunits == 0 || totalunits == 0 ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue == %llu || received_nValue == %llu || paidunits == %llu || totalunits == %llu\n",(long long)orig_nValue,(long long)received_nValue,(long long)paidunits,(long long)totalunits); return(false); } else if ( totalunits != (remaining_price + paidunits) ) { - fprintf(stderr,"ValidateAssetRemainder: totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); + fprintf(stderr,"ValidateAssetRemainder() totalunits %llu != %llu (remaining_price %llu + %llu paidunits)\n",(long long)totalunits,(long long)(remaining_price + paidunits),(long long)remaining_price,(long long)paidunits); return(false); } else if ( orig_nValue != (remaining_nValue + received_nValue) ) { - fprintf(stderr,"ValidateAssetRemainder: orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); + fprintf(stderr,"ValidateAssetRemainder() orig_nValue %llu != %llu (remaining_nValue %llu + %llu received_nValue)\n",(long long)orig_nValue,(long long)(remaining_nValue - received_nValue),(long long)remaining_nValue,(long long)received_nValue); return(false); } else @@ -222,10 +222,10 @@ bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int6 newunitprice = (remaining_nValue * COIN) / remaining_price; if ( recvunitprice < unitprice ) { - fprintf(stderr,"error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + fprintf(stderr,"ValidateAssetRemainder() error recvunitprice %.8f < %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); return(false); } - fprintf(stderr,"recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); + fprintf(stderr,"ValidateAssetRemainder() recvunitprice %.8f >= %.8f unitprice, new unitprice %.8f\n",(double)recvunitprice/(COIN*COIN),(double)unitprice/(COIN*COIN),(double)newunitprice/(COIN*COIN)); } return(true); } @@ -239,30 +239,30 @@ CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector origpubkey,st } */ -CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey) +vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey) { - CScript opret; + vscript_t vopret; uint8_t evalcode = EVAL_ASSETS; switch ( assetFuncId ) { //case 't': this cannot be here case 'x': case 'o': - opret << OP_RETURN << E_MARSHAL(ss << evalcode << assetFuncId); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId); break; case 's': case 'b': case 'S': case 'B': - opret << OP_RETURN << E_MARSHAL(ss << evalcode << assetFuncId << price << origpubkey); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << price << origpubkey); break; case 'E': case 'e': assetid2 = revuint256(assetid2); - opret << OP_RETURN << E_MARSHAL(ss << evalcode << assetFuncId << assetid2 << price << origpubkey); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << assetid2 << price << origpubkey); break; default: fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId); - opret << OP_RETURN; + //opret << OP_RETURN; break; } - return(opret); + return(vopret); } /* it is for compatibility, do not use this for new contracts (use DecodeTokenCreateOpRet) @@ -279,77 +279,77 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &o return(0); } */ -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey) +uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey) { - std::vector vopretExtra, vopretStripped; - uint8_t *script, funcId = 0, assetFuncId = 0, dummyEvalCode, dummyAssetFuncId; + vscript_t vopretAssets; //, vopretAssetsStripped; + uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId; uint256 dummyTokenid; std::vector voutPubkeysDummy; + std::vector> oprets; tokenid = zeroid; assetid2 = zeroid; price = 0; + assetsEvalCode = 0; + assetsFuncId = 0; // First - decode token opret: - funcId = DecodeTokenOpRet(scriptPubKey, evalCodeInOpret, tokenid, voutPubkeysDummy, vopretExtra); + funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, oprets); + GetOpretBlob(oprets, OPRETID_ASSETSDATA, vopretAssets); - /*GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if (script == 0) { - std::cerr << "DecodeAssetOpRet() script is empty" << std::endl; - return (uint8_t)0; - }*/ - //bool isEof = true; // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set! - //bool result = E_UNMARSHAL(vopret, ss >> evalCodeInOpret; ss >> funcId; ss >> tokenid; ss >> assetFuncId; isEof = ss.eof()); + LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl); - if (funcId == 0 || vopretExtra.size() < 2) { - std::cerr << "DecodeAssetOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretExtra.size()=" << vopretExtra.size() << std::endl; + if (funcId == 0 || vopretAssets.size() < 2) { + LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl); return (uint8_t)0; } - if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size - std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretStripped" << std::endl; - return (uint8_t)0; - } + //if (!E_UNMARSHAL(vopretAssets, { ss >> vopretAssetsStripped; })) { //strip string size + // std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretAssetsStripped" << std::endl; + // return (uint8_t)0; + //} - ////tokenid = revuint256(tokenid); already done in DecodeToken! - evalCodeInOpret = vopretStripped.begin()[0]; - assetFuncId = vopretStripped.begin()[1]; + // additional check to prevent crash + if (vopretAssets.size() >= 2) { - //std::cerr << "DecodeAssetOpRet() evalCodeInOpret=" << (int)evalCodeInOpret << " funcId=" << (char)(funcId ? funcId : ' ') << " assetFuncId=" << (char)(assetFuncId ? assetFuncId : ' ') << std::endl; + assetsEvalCode = vopretAssets.begin()[0]; + assetsFuncId = vopretAssets.begin()[1]; - if(evalCodeInOpret == EVAL_ASSETS) - { - //fprintf(stderr,"decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId); - switch( assetFuncId ) + LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() assetsEvalCode=" << (int)assetsEvalCode << " funcId=" << (char)(funcId ? funcId : ' ') << " assetsFuncId=" << (char)(assetsFuncId ? assetsFuncId : ' ') << std::endl); + + if (assetsEvalCode == EVAL_ASSETS) { - case 'x': case 'o': - if (vopretStripped.size() == 2) // no data after 'evalcode assetFuncId' allowed + //fprintf(stderr,"DecodeAssetTokenOpRet() decode.[%c] assetFuncId.[%c]\n", funcId, assetFuncId); + switch (assetsFuncId) + { + case 'x': case 'o': + if (vopretAssets.size() == 2) // no data after 'evalcode assetFuncId' allowed { - return(assetFuncId); + return(assetsFuncId); } break; case 's': case 'b': case 'S': case 'B': - if (E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) + if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0) { - //fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price); - return(assetFuncId); + //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); + return(assetsFuncId); } break; case 'E': case 'e': - if ( E_UNMARSHAL(vopretStripped, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 ) + if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0) { - //fprintf(stderr,"DecodeAssetTokenOpRet got price %llu\n",(long long)price); + //fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price); assetid2 = revuint256(assetid2); - return(assetFuncId); + return(assetsFuncId); } break; default: - fprintf(stderr,"DecodeAssetTokenOpRet: illegal assetFuncId.%02x\n", assetFuncId); - //funcId = 0; break; + } } } + + LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl); return (uint8_t)0; } @@ -358,49 +358,53 @@ bool SetAssetOrigpubkey(std::vector &origpubkey,int64_t &price,const CT { uint256 assetid,assetid2; uint8_t evalCode; + if ( tx.vout.size() > 0 && DecodeAssetTokenOpRet(tx.vout[tx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey) != 0 ) return(true); else return(false); } -// Calculate sell/buy owner's source token/asset address from ask/bid tx -bool GetAssetorigaddrs(struct CCcontract_info *cp, char *userCCaddr, char *destaddr, const CTransaction& tx) +// Calculate seller/buyer's dest cc address from ask/bid tx funcid +bool GetAssetorigaddrs(struct CCcontract_info *cp, char *origCCaddr, char *origNormalAddr, const CTransaction& vintx) { - uint256 assetid,assetid2; int64_t price,nValue=0; int32_t n; uint8_t funcid; + uint256 assetid, assetid2; + int64_t price,nValue=0; + int32_t n; + uint8_t vintxFuncId; std::vector origpubkey; CScript script; uint8_t evalCode; - n = tx.vout.size(); - if( n == 0 || (funcid = DecodeAssetTokenOpRet(tx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) + n = vintx.vout.size(); + if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 ) return(false); bool bGetCCaddr = false; - if (funcid == 's' || funcid == 'S') { - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey)); - //bGetCCaddr = GetTokensCCaddress(cp, CCaddr, pubkey2pk(origpubkey)); + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + + if (vintxFuncId == 's' || vintxFuncId == 'S') { + // bGetCCaddr = GetCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); + cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present + bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible } - else if (funcid == 'b' || funcid == 'B') { - struct CCcontract_info *cpTokens, tokensC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - bGetCCaddr = GetCCaddress(cpTokens, userCCaddr, pubkey2pk(origpubkey)); + else if (vintxFuncId == 'b' || vintxFuncId == 'B') { + cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present + bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible } else { - std::cerr << "GetAssetorigaddrs incorrect funcid=" << (char)(funcid?funcid:' ') << std::endl; + std::cerr << "GetAssetorigaddrs incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl; return false; } - - if( bGetCCaddr && Getscriptaddress(destaddr, CScript() << origpubkey << OP_CHECKSIG)) + if( bGetCCaddr && Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG)) return(true); else return(false); } -int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx) +int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx) { uint256 hashBlock; uint256 assetid, assetid2; @@ -410,7 +414,7 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch char destaddr[64], unspendableAddr[64]; - origaddr[0] = destaddr[0] = CCaddr[0] = 0; + origaddr[0] = destaddr[0] = origCCaddr[0] = 0; uint8_t funcid = 0; if (tx.vout.size() > 0) { @@ -418,6 +422,7 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch int64_t tmpprice; std::vector tmporigpubkey; uint8_t evalCode; + funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey); } @@ -427,45 +432,47 @@ int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,ch return eval->Invalid("vin1 needs to be buyvin.vout[0]"); else if( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash, vinTx,hashBlock) == 0 ) { - /* int32_t z; - for (z=31; z>=0; z--) - fprintf(stderr,"%02x",((uint8_t *)&tx.vin[vini].prevout.hash)[z]); - fprintf(stderr," vini.%d\n",vini); */ - std::cerr << "AssetValidateCCvin cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl; + std::cerr << "AssetValidateCCvin() cannot load vintx for vin=" << vini << " vintx id=" << tx.vin[vini].prevout.hash.GetHex() << std::endl; return eval->Invalid("always should find CCvin, but didnt"); } - // if fillSell or cancelSell --> to spend tokens from dual-eval token-assets unspendable addr + // check source cc unspendable cc address: + // if fillSell or cancelSell --> should spend tokens from dual-eval token-assets unspendable addr else if( (funcid == 'S' || funcid == 'x') && (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || !GetTokensCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || strcmp(destaddr, unspendableAddr) != 0)) { - fprintf(stderr,"AssetValidateCCvin cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); - return eval->Invalid("invalid vin AssetsCCaddr"); + fprintf(stderr,"AssetValidateCCvin() cc addr %s is not dual token-evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); + return eval->Invalid("invalid vin assets CCaddr"); } - // if fillBuy or cancelBuy --> to spend coins from asset unspendable addr + // if fillBuy or cancelBuy --> should spend coins from asset unspendable addr else if ((funcid == 'B' || funcid == 'o') && (Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || !GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) || strcmp(destaddr, unspendableAddr) != 0)) { - fprintf(stderr, "AssetValidateCCvin cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); - return eval->Invalid("invalid vin AssetsCCaddr"); + fprintf(stderr, "AssetValidateCCvin() cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr); + return eval->Invalid("invalid vin assets CCaddr"); } - + // end of check source unspendable cc address //else if ( vinTx.vout[0].nValue < 10000 ) // return eval->Invalid("invalid dust for buyvin"); - else if( GetAssetorigaddrs(cp, CCaddr, origaddr, vinTx) == 0 ) + // get user dest cc and normal addresses: + else if( GetAssetorigaddrs(cp, origCCaddr, origaddr, vinTx) == 0 ) return eval->Invalid("couldnt get origaddr for buyvin"); - fprintf(stderr,"AssetValidateCCvin got %.8f to origaddr.(%s)\n",(double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr); + + //fprintf(stderr,"AssetValidateCCvin() got %.8f to origaddr.(%s)\n", (double)vinTx.vout[tx.vin[vini].prevout.n].nValue/COIN,origaddr); + if ( vinTx.vout[0].nValue == 0 ) return eval->Invalid("null value CCvin"); + return(vinTx.vout[0].nValue); } int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid) { CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode; + CCaddr[0] = origaddr[0] = 0; // validate locked coins on Assets vin[1] @@ -473,7 +480,7 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr return(0); else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) return eval->Invalid("invalid normal vout0 for buyvin"); - else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' && + else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' && vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'? return eval->Invalid("invalid normal vout1 for buyvin"); else @@ -493,7 +500,7 @@ int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmppr int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid) { CTransaction vinTx; int64_t nValue,assetoshis; - //fprintf(stderr,"AssetValidateSellvin\n"); + //fprintf(stderr,"AssetValidateSellvin()\n"); if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 ) return(0); if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 ) @@ -514,7 +521,7 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0) { - std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned null for the opret for txid=" << tx.GetHash().GetHex() << std::endl; + std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl; return(false); } /* it is now on token level: @@ -556,22 +563,20 @@ bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &pr } // Checks if the vout is a really Asset CC vout -// compareTotals == true, the func also validates the passed transaction itself: -// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid) { //std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl; + int32_t n = tx.vout.size(); + // just check boundaries: + if (v >= n - 1) { // just moved this up (dimxy) + std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl; + return(0); + } + if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here { - int32_t n = tx.vout.size(); - // just check boundaries: - if (v >= n - 1) { // just moved this up (dimxy) - std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl; - return(0); - } - // moved opret checking to this new reusable func (dimxy): const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey); //std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl; @@ -620,22 +625,19 @@ bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t assetoshis = IsTokensvout(false, false, cpTokens, NULL, vinTx, tx.vin[i].prevout.n, assetid); if (assetoshis != 0) { - std::cerr << "AssetCalcAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl; + //std::cerr << "AssetCalcAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl; inputs += assetoshis; } } } } - for (int32_t i = 0; i < numvouts; i++) + for (int32_t i = 0; i < numvouts-1; i++) { - // Note: we pass in here 'false' because we don't need to call AssetExactAmounts() recursively from IsAssetvout - // indeed, in this case we'll be checking this tx again assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid); - if (assetoshis != 0) { - std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl; + //std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl; outputs += assetoshis; } } @@ -643,12 +645,6 @@ bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t //std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; /* we do not verify inputs == outputs here, - it's done in Tokens: - if (inputs != outputs) { - if (tx.GetHash() != assetid) { - std::cerr << "AssetCalcAmounts() unequal inputs=" << inputs << " vs outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl; - return (!eval) ? false : eval->Invalid("assets cc inputs != cc outputs"); - } - } */ + it's now done in Tokens */ return(true); } diff --git a/src/cc/CCassetstx.cpp b/src/cc/CCassetstx.cpp index 9d83beb2c..0929591ff 100644 --- a/src/cc/CCassetstx.cpp +++ b/src/cc/CCassetstx.cpp @@ -14,174 +14,145 @@ ******************************************************************************/ #include "CCassets.h" -//#include "CCtokens.h" +#include "CCtokens.h" -int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs) -{ - char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t j,vout,n = 0; - std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); - - threshold = total/(maxinputs!=0?maxinputs:64); // TODO: is maxinputs really not over 64, what if i want to calc total balance? - - 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; - - for (j=0; junspendableCCaddr) != 0 && strcmp(destaddr,cp->unspendableaddr2) != 0 ) - continue; - fprintf(stderr,"AddAssetInputs() check destaddress=%s vout amount=%.8f\n",destaddr,(double)vintx.vout[vout].nValue/COIN); - if( (nValue = IsAssetvout(cp, price, origpubkey, vintx, vout, assetid)) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) - { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - //std::cerr << "AddAssetInputs() adding input nValue=" << nValue << std::endl; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } - - //std::cerr << "AddAssetInputs() found totalinputs=" << totalinputs << std::endl; - return(totalinputs); -} - - -UniValue AssetOrders(uint256 refassetid) +UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t additionalEvalCode) { static uint256 zero; UniValue result(UniValue::VARR); - std::vector > unspentOutputsTokens, unspentOutputsAssets; - - struct CCcontract_info *cpTokens, tokensC; - struct CCcontract_info *cpAssets, assetsC; - cpTokens = CCinit(&tokensC, EVAL_TOKENS); - cpAssets = CCinit(&assetsC, EVAL_ASSETS); + struct CCcontract_info *cpAssets, assetsC; + struct CCcontract_info *cpTokens, tokensC; + + cpAssets = CCinit(&assetsC, EVAL_ASSETS); + cpTokens = CCinit(&tokensC, EVAL_TOKENS); auto addOrders = [&](struct CCcontract_info *cp, std::vector >::const_iterator it) { uint256 txid, hashBlock, assetid, assetid2; int64_t price; std::vector origpubkey; - CTransaction vintx; + CTransaction ordertx; uint8_t funcid, evalCode; - char numstr[32], funcidstr[16], origaddr[64], assetidstr[65]; + char numstr[32], funcidstr[16], origaddr[64], origtokenaddr[64], assetidstr[65]; txid = it->first.txhash; - //std::cerr << "addOrders() txid=" << txid.GetHex() << std::endl; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking txid=" << txid.GetHex() << std::endl); + if ( GetTransaction(txid, ordertx, hashBlock, false) != 0 ) { // for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey); - //std::cerr << "addOrders() vintx.vout.size()=" << vintx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl; - if (vintx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0) + if (ordertx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(ordertx.vout[ordertx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0) { - if (refassetid != zero && assetid != refassetid) - { - //int32_t z; - //for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&txid)[z]); - //fprintf(stderr," txid\n"); - //for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&assetid)[z]); - //fprintf(stderr," assetid\n"); - //for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&refassetid)[z]); - //fprintf(stderr," refassetid\n"); - return; - } + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking ordertx.vout.size()=" << ordertx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl); - //std::cerr << "addOrders() it->first.index=" << it->first.index << " vintx.vout[it->first.index].nValue=" << vintx.vout[it->first.index].nValue << std::endl; - if (vintx.vout[it->first.index].nValue == 0) - return; + if (refassetid != zero && assetid == refassetid || + pk != CPubKey() && pk == pubkey2pk(origpubkey) && (funcid == 'S' || funcid == 's')) + { - UniValue item(UniValue::VOBJ); + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << std::endl); + if (ordertx.vout[it->first.index].nValue == 0) { + LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() order with value=0 skipped" << std::endl); + return; + } - funcidstr[0] = funcid; - funcidstr[1] = 0; - item.push_back(Pair("funcid", funcidstr)); - item.push_back(Pair("txid", uint256_str(assetidstr,txid))); - item.push_back(Pair("vout", (int64_t)it->first.index)); - if ( funcid == 'b' || funcid == 'B' ) - { - sprintf(numstr,"%.8f",(double)vintx.vout[it->first.index].nValue/COIN); - item.push_back(Pair("amount",numstr)); - sprintf(numstr,"%.8f",(double)vintx.vout[0].nValue/COIN); - item.push_back(Pair("bidamount",numstr)); - } - else - { - sprintf(numstr,"%llu",(long long)vintx.vout[it->first.index].nValue); - item.push_back(Pair("amount",numstr)); - sprintf(numstr,"%llu",(long long)vintx.vout[0].nValue); - item.push_back(Pair("askamount",numstr)); - } - if ( origpubkey.size() == 33 ) - { - GetCCaddress(cp, origaddr, pubkey2pk(origpubkey)); // TODO: what is this? is it asset or token?? - item.push_back(Pair("origaddress", origaddr)); - } - if ( assetid != zeroid ) - item.push_back(Pair("tokenid",uint256_str(assetidstr,assetid))); - if ( assetid2 != zeroid ) - item.push_back(Pair("otherid",uint256_str(assetidstr,assetid2))); - if ( price > 0 ) - { - if ( funcid == 's' || funcid == 'S' || funcid == 'e' || funcid == 'e' ) + UniValue item(UniValue::VOBJ); + + funcidstr[0] = funcid; + funcidstr[1] = 0; + item.push_back(Pair("funcid", funcidstr)); + item.push_back(Pair("txid", uint256_str(assetidstr, txid))); + item.push_back(Pair("vout", (int64_t)it->first.index)); + if (funcid == 'b' || funcid == 'B') { - sprintf(numstr,"%.8f",(double)price / COIN); - item.push_back(Pair("totalrequired", numstr)); - sprintf(numstr,"%.8f",(double)price / (COIN * vintx.vout[0].nValue)); - item.push_back(Pair("price", numstr)); + sprintf(numstr, "%.8f", (double)ordertx.vout[it->first.index].nValue / COIN); + item.push_back(Pair("amount", numstr)); + sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / COIN); + item.push_back(Pair("bidamount", numstr)); } else { - item.push_back(Pair("totalrequired", (int64_t)price)); - sprintf(numstr,"%.8f",(double)vintx.vout[0].nValue / (price * COIN)); - item.push_back(Pair("price",numstr)); + sprintf(numstr, "%llu", (long long)ordertx.vout[it->first.index].nValue); + item.push_back(Pair("amount", numstr)); + sprintf(numstr, "%llu", (long long)ordertx.vout[0].nValue); + item.push_back(Pair("askamount", numstr)); } + if (origpubkey.size() == 33) + { + GetCCaddress(cp, origaddr, pubkey2pk(origpubkey)); + item.push_back(Pair("origaddress", origaddr)); + GetTokensCCaddress(cpTokens, origtokenaddr, pubkey2pk(origpubkey)); + item.push_back(Pair("origtokenaddress", origtokenaddr)); + + } + if (assetid != zeroid) + item.push_back(Pair("tokenid", uint256_str(assetidstr, assetid))); + if (assetid2 != zeroid) + item.push_back(Pair("otherid", uint256_str(assetidstr, assetid2))); + if (price > 0) + { + if (funcid == 's' || funcid == 'S' || funcid == 'e' || funcid == 'e') + { + sprintf(numstr, "%.8f", (double)price / COIN); + item.push_back(Pair("totalrequired", numstr)); + sprintf(numstr, "%.8f", (double)price / (COIN * ordertx.vout[0].nValue)); + item.push_back(Pair("price", numstr)); + } + else + { + item.push_back(Pair("totalrequired", (int64_t)price)); + sprintf(numstr, "%.8f", (double)ordertx.vout[0].nValue / (price * COIN)); + item.push_back(Pair("price", numstr)); + } + } + result.push_back(item); + LOGSTREAM("ccassets", CCLOG_DEBUG1, stream << "addOrders() added order funcId=" << (char)(funcid ? funcid : ' ') << " it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << " tokenid=" << assetid.GetHex() << std::endl); } - result.push_back(item); - //fprintf(stderr,"addOrders() func.(%c) %s/v%d %.8f\n",funcid,uint256_str(assetidstr,txid),(int32_t)it->first.index,(double)vintx.vout[it->first.index].nValue/COIN); } } }; + std::vector > unspentOutputsTokens, unspentOutputsDualEvalTokens, unspentOutputsCoins; char assetsUnspendableAddr[64]; GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsAssets, assetsUnspendableAddr /*(char *)cpTokens->unspendableCCaddr*/); + SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr,true); - char tokensUnspendableAddr[64]; - GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - SetCCunspents(unspentOutputsAssets, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/); + char assetsTokensUnspendableAddr[64]; + std::vector vopretNonfungible; + if (refassetid != zeroid) { + GetNonfungibleData(refassetid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + } + GetTokensCCaddress(cpAssets, assetsTokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); + SetCCunspents(unspentOutputsTokens, assetsTokensUnspendableAddr,true); - for (std::vector >::const_iterator itTokens = unspentOutputsTokens.begin(); + // tokenbids: + for (std::vector >::const_iterator itCoins = unspentOutputsCoins.begin(); + itCoins != unspentOutputsCoins.end(); + itCoins++) + addOrders(cpAssets, itCoins); + + // tokenasks: + for (std::vector >::const_iterator itTokens = unspentOutputsTokens.begin(); itTokens != unspentOutputsTokens.end(); itTokens++) - addOrders(cpTokens, itTokens); - - for (std::vector >::const_iterator itAssets = unspentOutputsAssets.begin(); - itAssets != unspentOutputsAssets.end(); - itAssets++) - addOrders(cpAssets, itAssets); + addOrders(cpAssets, itTokens); + if (additionalEvalCode != 0) { //this would be mytokenorders + char assetsDualEvalTokensUnspendableAddr[64]; + + // try also dual eval tokenasks (and we do not need bids): + cpAssets->additionalTokensEvalcode2 = additionalEvalCode; + GetTokensCCaddress(cpAssets, assetsDualEvalTokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); + SetCCunspents(unspentOutputsDualEvalTokens, assetsDualEvalTokensUnspendableAddr,true); + + for (std::vector >::const_iterator itDualEvalTokens = unspentOutputsDualEvalTokens.begin(); + itDualEvalTokens != unspentOutputsDualEvalTokens.end(); + itDualEvalTokens++) + addOrders(cpAssets, itDualEvalTokens); + } return(result); } @@ -328,14 +299,14 @@ std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, in return (""); } - CPubKey unspendablePubkey = GetUnspendable(cpAssets, 0); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendablePubkey)); + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendableAssetsPubkey)); mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); std::vector voutTokenPubkeys; // should be empty - no token vouts - return(FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); + return FinalizeCCTx(0, cpAssets, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret. + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); // But still such token opret should not make problems because no token eval in these vouts } CCerror = strprintf("no coins found to make buy offer"); return(""); @@ -348,7 +319,6 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p CPubKey mypk; uint64_t mask; int64_t inputs, CCchange; - CScript opret; struct CCcontract_info *cpAssets, assetsC; struct CCcontract_info *cpTokens, tokensC; @@ -359,7 +329,8 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p return(""); } - cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: this is for signing + cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: for signing + if (txfee == 0) txfee = 10000; @@ -367,10 +338,11 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p mypk = pubkey2pk(Mypubkey()); if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) { + std::vector vopretNonfungible; mask = ~((1LL << mtx.vin.size()) - 1); - // add single-eval tokens: - cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: tokens is here - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60)) > 0) + // add single-eval tokens (or non-fungible tokens): + cpTokens = CCinit(&tokensC, EVAL_TOKENS); // NOTE: adding inputs only from EVAL_TOKENS cc + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, askamount, 60, vopretNonfungible)) > 0) { if (inputs < askamount) { //was: askamount = inputs; @@ -379,20 +351,26 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p return (""); } - CPubKey unspendablePubkey = GetUnspendable(cpAssets, NULL); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, askamount, unspendablePubkey)); - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker + // if this is non-fungible tokens: + if( !vopretNonfungible.empty() ) + // set its evalcode + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + + CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, NULL); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, cpAssets->additionalTokensEvalcode2, askamount, unspendableAssetsPubkey)); + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk)); //marker (seems, it is not for tokenorders) if (inputs > askamount) CCchange = (inputs - askamount); if (CCchange != 0) - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // change to single-eval token vout + // change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible) + mtx.vout.push_back(MakeTokensCC1vout((cpAssets->additionalTokensEvalcode2) ? cpAssets->additionalTokensEvalcode2 : EVAL_TOKENS, CCchange, mypk)); std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(unspendablePubkey); + voutTokenPubkeys.push_back(unspendableAssetsPubkey); - opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())); - return(FinalizeCCTx(mask,cpAssets, mtx, mypk, txfee, opret)); + return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey())))); } else { fprintf(stderr, "need some tokens to place ask\n"); @@ -430,7 +408,7 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { mask = ~((1LL << mtx.vin.size()) - 1); - if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) + /*if ((inputs = AddAssetInputs(cp, mtx, mypk, assetid, askamount, 60)) > 0) { ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// if (inputs < askamount) { @@ -458,13 +436,13 @@ std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 a else { opret = EncodeTokenOpRet(assetid, voutTokenPubkeys, EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey())); - } + } ////////////////////////// NOT IMPLEMENTED YET///////////////////////////////// return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret)); } else { fprintf(stderr, "need some assets to place ask\n"); - } + } */ } else { // dimxy added 'else', because it was misleading message before fprintf(stderr,"need some native coins to place ask\n"); @@ -494,12 +472,14 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0) { + std::vector vopretNonfungible; + GetNonfungibleData(assetid, vopretNonfungible); + bidamount = vintx.vout[0].nValue; mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) { - if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b' else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B' } @@ -510,8 +490,8 @@ std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid) std::vector voutTokenPubkeys; // should be empty, no token vouts return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('o', zeroid, 0, Mypubkey())))); + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('o', zeroid, 0, Mypubkey()))))); } } return(""); @@ -523,8 +503,12 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t askamount; - CPubKey mypk; struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; - uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector dummyOrigpubkey; + CPubKey mypk; + struct CCcontract_info *cpTokens, *cpAssets, tokensC, assetsC; + uint8_t funcid, dummyEvalCode; + uint256 dummyAssetid, dummyAssetid2; + int64_t dummyPrice; + std::vector dummyOrigpubkey; cpAssets = CCinit(&assetsC, EVAL_ASSETS); @@ -538,27 +522,29 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) { + std::vector vopretNonfungible; + GetNonfungibleData(assetid, vopretNonfungible); + askamount = vintx.vout[0].nValue; mtx.vin.push_back(CTxIn(asktxid, 0, CScript())); if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0) { - if (funcid == 's') mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' - else if (funcid=='S') mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' + if (funcid == 's') + mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s' + else if (funcid=='S') + mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S' } - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, askamount, mypk)); // one-eval token vout + if (vopretNonfungible.size() > 0) + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + + mtx.vout.push_back(MakeTokensCC1vout(cpAssets->additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : cpAssets->additionalTokensEvalcode2, askamount, mypk)); // one-eval token vout mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(mypk); - /* char myCCaddr[65]; - uint8_t myPrivkey[32]; - Myprivkey(myPrivkey); - cpAssets = CCinit(&assetsC, EVAL_ASSETS); - GetCCaddress(cpAssets, myCCaddr, mypk); */ - // this is only for unspendable addresses: //CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress @@ -572,8 +558,8 @@ std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid) CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr); return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('x', zeroid, 0, Mypubkey())))); + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('x', zeroid, 0, Mypubkey()))))); } } return(""); @@ -614,8 +600,9 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f SetAssetOrigpubkey(origpubkey, origprice, vintx); mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable - - if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60)) > 0) + + std::vector vopretNonfungible; + if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60, vopretNonfungible)) > 0) { if (inputs < fillamount) { std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl; @@ -624,6 +611,10 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f } SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice); + + uint8_t additionalTokensEvalcode2 = 0; + if (vopretNonfungible.size() > 0) + additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; if (inputs > fillamount) CCchange = (inputs - fillamount); @@ -634,13 +625,13 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the buyer - mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); // vout3 marker to origpubkey + mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the originator + mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, origpubkey)); // vout3 marker to origpubkey if (CCchange != 0) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens - fprintf(stderr,"FillBuyOffer remaining %llu -> origpubkey\n", (long long)remaining_required); + fprintf(stderr,"FillBuyOffer() remaining %llu -> origpubkey\n", (long long)remaining_required); char unspendableAssetsAddr[64]; cpAssets = CCinit(&assetsC, EVAL_ASSETS); @@ -653,9 +644,9 @@ std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t f std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(pubkey2pk(origpubkey)); - return(FinalizeCCTx(mask,cpTokens,mtx,mypk,txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey)))); + return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee, + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey))))); } else return("dont have any assets to fill bid"); } } @@ -691,15 +682,21 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a return(""); } + std::vector vopretNonfungible; + uint8_t additionalTokensEvalcode2 = 0; + GetNonfungibleData(assetid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + cpAssets = CCinit(&assetsC, EVAL_ASSETS); if (txfee == 0) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) - { - mask = ~((1LL << mtx.vin.size()) - 1); + //if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0) + //{ + //mask = ~((1LL << mtx.vin.size()) - 1); if (GetTransaction(asktxid, vintx, hashBlock, false) != 0) { orig_assetoshis = vintx.vout[askvout].nValue; @@ -707,13 +704,12 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a dprice = (double)total_nValue / orig_assetoshis; paid_nValue = dprice * fillunits; - mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); // NOTE: this is the reference to tokens -> send cpTokens for signing into FinalizeCCTx! - - if (assetid2 != zeroid) - inputs = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet + if (assetid2 != zeroid) { + inputs = 0; // = AddAssetInputs(cpAssets, mtx, mypk, assetid2, paid_nValue, 60); // not implemented yet + } else { - inputs = AddNormalinputs(mtx, mypk, paid_nValue, 60); + inputs = AddNormalinputs(mtx, mypk, 2 * txfee + paid_nValue, 60); // Better to use single AddNormalinputs() to allow payment if user has only single utxo with normal funds mask = ~((1LL << mtx.vin.size()) - 1); } if (inputs > 0) @@ -723,7 +719,10 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a CCerror = strprintf("insufficient coins to fill sell"); return (""); } - + + // cc vin should be after normal vin + mtx.vin.push_back(CTxIn(asktxid, askvout, CScript())); + if (assetid2 != zeroid) SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet else @@ -732,11 +731,11 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a if (assetid2 != zeroid && inputs > paid_nValue) CCchange = (inputs - paid_nValue); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); // vout.0 tokens remainder to unspendable cc addr - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, received_assetoshis, mypk)); //vout.1 purchased tokens to self single-eval addr + // vout.0 tokens remainder to unspendable cc addr: + mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, additionalTokensEvalcode2, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL))); + //vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr: + mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, received_assetoshis, mypk)); - // NOTE: no marker here - if (assetid2 != zeroid) { std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl; // TODO: change MakeCC1vout appropriately when implementing: @@ -768,14 +767,16 @@ std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 a std::vector voutTokenPubkeys; voutTokenPubkeys.push_back(mypk); + cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2; + return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee, - EncodeTokenOpRet(assetid, voutTokenPubkeys, - EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey)))); + EncodeTokenOpRet(assetid, voutTokenPubkeys, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey))))); } else { CCerror = strprintf("filltx not enough utxos"); fprintf(stderr,"%s\n", CCerror.c_str()); } } - } + //} return(""); } diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 577332b63..6c66b3599 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -31,6 +31,7 @@ #include "CCPayments.h" #include "CCGateways.h" #include "CCtokens.h" +#include "CCImportGateway.h" /* CCcustom has most of the functions that need to be extended to create a new CC contract. @@ -63,7 +64,6 @@ const char *AssetsCCaddr = "RGKRjeTBw4LYFotSDLT6RWzMHbhXri6BG6"; const char *AssetsNormaladdr = "RFYE2yL3KknWdHK6uNhvWacYsCUtwzjY3u"; char AssetsCChexstr[67] = { "02adf84e0e075cf90868bd4e3d34a03420e034719649c41f371fc70d8e33aa2702" }; uint8_t AssetsCCpriv[32] = { 0x9b, 0x17, 0x66, 0xe5, 0x82, 0x66, 0xac, 0xb6, 0xba, 0x43, 0x83, 0x74, 0xf7, 0x63, 0x11, 0x3b, 0xf0, 0xf3, 0x50, 0x6f, 0xd9, 0x6b, 0x67, 0x85, 0xf9, 0x7a, 0xf0, 0x54, 0x4d, 0xb1, 0x30, 0x77 }; - #include "CCcustom.inc" #undef FUNCNAME #undef EVALCODE @@ -75,7 +75,6 @@ const char *FaucetCCaddr = "R9zHrofhRbub7ER77B7NrVch3A63R39GuC"; const char *FaucetNormaladdr = "RKQV4oYs4rvxAWx1J43VnT73rSTVtUeckk"; char FaucetCChexstr[67] = { "03682b255c40d0cde8faee381a1a50bbb89980ff24539cb8518e294d3a63cefe12" }; uint8_t FaucetCCpriv[32] = { 0xd4, 0x4f, 0xf2, 0x31, 0x71, 0x7d, 0x28, 0x02, 0x4b, 0xc7, 0xdd, 0x71, 0xa0, 0x39, 0xc4, 0xbe, 0x1a, 0xfe, 0xeb, 0xc2, 0x46, 0xda, 0x76, 0xf8, 0x07, 0x53, 0x3d, 0x96, 0xb4, 0xca, 0xa0, 0xe9 }; - #include "CCcustom.inc" #undef FUNCNAME #undef EVALCODE @@ -243,28 +242,67 @@ uint8_t CClibCCpriv[32] = { 0x57, 0xcf, 0x49, 0x71, 0x7d, 0xb4, 0x15, 0x1b, 0x4f #undef FUNCNAME #undef EVALCODE +// ImportGateway +#define FUNCNAME IsImportGatewayInput +#define EVALCODE EVAL_IMPORTGATEWAY +const char *ImportGatewayCCaddr = "RXJT6CRAXHFuQ2UjqdxMj7EfrayF6UJpzZ"; +const char *ImportGatewayNormaladdr = "RNFRho63Ddz1Rh2eGPETykrU4fA8r67S4Y"; +char ImportGatewayCChexstr[67] = { "0397231cfe04ea32d5fafb2206773ec9fba6e15c5a4e86064468bca195f7542714" }; +uint8_t ImportGatewayCCpriv[32] = { 0x65, 0xef, 0x27, 0xeb, 0x3d, 0xb0, 0xb4, 0xae, 0x0f, 0xbc, 0x77, 0xdb, 0xf8, 0x40, 0x48, 0x90, 0x52, 0x20, 0x9e, 0x45, 0x3b, 0x49, 0xd8, 0x97, 0x60, 0x8c, 0x27, 0x4c, 0x59, 0x46, 0xe1, 0xdf }; +#include "CCcustom.inc" +#undef FUNCNAME +#undef EVALCODE + int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode) { - CPubKey pk; uint8_t pub33[33]; char CCaddr[64]; + CPubKey pk; int32_t i; uint8_t pub33[33],check33[33],hash[32]; char CCaddr[64],checkaddr[64],str[67]; + cp->evalcode = evalcode; + cp->ismyvin = IsCClibInput; + memcpy(cp->CCpriv,CClibCCpriv,32); if ( evalcode == EVAL_FIRSTUSER ) // eventually make a hashchain for each evalcode { - cp->evalcode = evalcode; - cp->ismyvin = IsCClibInput; strcpy(cp->CChexstr,CClibCChexstr); - memcpy(cp->CCpriv,CClibCCpriv,32); decode_hex(pub33,33,cp->CChexstr); pk = buf2pk(pub33); Getscriptaddress(cp->normaladdr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG); if ( strcmp(cp->normaladdr,CClibNormaladdr) != 0 ) fprintf(stderr,"CClib_initcp addr mismatch %s vs %s\n",cp->normaladdr,CClibNormaladdr); GetCCaddress(cp,cp->unspendableCCaddr,pk); - return(0); + if ( priv2addr(checkaddr,check33,cp->CCpriv) != 0 ) + { + if ( buf2pk(check33) == pk && strcmp(checkaddr,cp->normaladdr) == 0 ) + { + //fprintf(stderr,"verified evalcode.%d %s %s\n",cp->evalcode,checkaddr,pubkey33_str(str,pub33)); + return(0); + } else fprintf(stderr,"CClib_initcp mismatched privkey -> addr %s vs %s\n",checkaddr,cp->normaladdr); + } + } + else + { + for (i=EVAL_FIRSTUSER; iCCpriv,32); + memcpy(cp->CCpriv,hash,32); + } + if ( priv2addr(cp->normaladdr,pub33,cp->CCpriv) != 0 ) + { + pk = buf2pk(pub33); + for (i=0; i<33; i++) + sprintf(&cp->CChexstr[i*2],"%02x",pub33[i]); + cp->CChexstr[i*2] = 0; + GetCCaddress(cp,cp->unspendableCCaddr,pk); + //printf("evalcode.%d initialized\n",evalcode); + return(0); + } } return(-1); } struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) { + // important to clear because not all members are always initialized! + memset(cp, '\0', sizeof(*cp)); + cp->evalcode = evalcode; switch ( evalcode ) { @@ -397,6 +435,14 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->validate = TokensValidate; cp->ismyvin = IsTokensInput; break; + case EVAL_IMPORTGATEWAY: + strcpy(cp->unspendableCCaddr, ImportGatewayCCaddr); + strcpy(cp->normaladdr, ImportGatewayNormaladdr); + strcpy(cp->CChexstr, ImportGatewayCChexstr); + memcpy(cp->CCpriv, ImportGatewayCCpriv, 32); + cp->validate = ImportGatewayValidate; + cp->ismyvin = IsImportGatewayInput; + break; default: if ( CClib_initcp(cp,evalcode) < 0 ) return(0); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 66c001969..5b6d575f3 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -42,6 +42,7 @@ one other technical note is that komodod has the insight-explorer extensions bui #include #include "../script/standard.h" #include "../base58.h" +#include "../key.h" #include "../core_io.h" #include "../script/sign.h" #include "../wallet/wallet.h" @@ -50,8 +51,9 @@ one other technical note is that komodod has the insight-explorer extensions bui #include "../komodo_defs.h" #include "../utlist.h" #include "../uthash.h" +#include "merkleblock.h" - +#define CC_BURNPUBKEY "02deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead" #define CC_MAXVINS 1024 #define SMALLVAL 0.000000000000001 @@ -66,6 +68,29 @@ one other technical note is that komodod has the insight-explorer extensions bui #include "../komodo_cJSON.h" +// token opret additional data block ids: + enum opretid : uint8_t { + // cc contracts data: + OPRETID_NONFUNGIBLEDATA = 0x11, + OPRETID_ASSETSDATA = 0x12, + OPRETID_GATEWAYSDATA = 0x13, + OPRETID_CHANNELSDATA = 0x14, + OPRETID_HEIRDATA = 0x15, + OPRETID_ROGUEGAMEDATA = 0x16, + + // non cc contract data: + OPRETID_FIRSTNONCCDATA = 0x80, + OPRETID_BURNDATA = 0x80, + OPRETID_IMPORTDATA = 0x81 +}; + + // find opret blob by opretid + inline bool GetOpretBlob(const std::vector>> &oprets, uint8_t id, std::vector &vopret) { + vopret.clear(); + for(auto p : oprets) if (p.first == id) { vopret = p.second; return true; } + return false; + } + struct CC_utxo { uint256 txid; @@ -88,13 +113,14 @@ struct CCcontract_info { // this is for spending from 'unspendable' CC address uint8_t evalcode; + uint8_t additionalTokensEvalcode2; // this is for making three-eval-token vouts (EVAL_TOKENS + evalcode + additionalEvalcode2) char unspendableCCaddr[64], CChexstr[72], normaladdr[64]; uint8_t CCpriv[32]; // this for 1of2 keys coins cryptocondition (for this evalcode) // NOTE: only one evalcode is allowed at this time char coins1of2addr[64]; - CPubKey coins1of2pk[2]; + CPubKey coins1of2pk[2]; uint8_t coins1of2priv[32]; // the same for tokens 1of2 keys cc char tokens1of2addr[64]; @@ -102,7 +128,7 @@ struct CCcontract_info // this is for spending from two additional 'unspendable' CC addresses of other eval codes // (that is, for spending from several cc contract 'unspendable' addresses): - uint8_t evalcode2, evalcode3; + uint8_t unspendableEvalcode2, unspendableEvalcode3; // changed evalcodeN to unspendableEvalcodeN for not mixing up with additionalEvalcodeN char unspendableaddr2[64], unspendableaddr3[64]; uint8_t unspendablepriv2[32], unspendablepriv3[32]; CPubKey unspendablepk2, unspendablepk3; @@ -121,6 +147,8 @@ struct oracleprice_info int32_t height; }; +typedef std::vector vscript_t; + #ifdef ENABLE_WALLET extern CWallet* pwalletMain; #endif @@ -131,54 +159,70 @@ int32_t komodo_nextheight(); int32_t CCgetspenttxid(uint256 &spenttxid,int32_t &vini,int32_t &height,uint256 txid,int32_t vout); void CCclearvars(struct CCcontract_info *cp); -UniValue CClib(struct CCcontract_info *cp,char *method,cJSON *params); +UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr); UniValue CClib_info(struct CCcontract_info *cp); +CBlockIndex *komodo_blockindex(uint256 hash); +CBlockIndex *komodo_chainactive(int32_t height); +int32_t komodo_blockheight(uint256 hash); +void StartShutdown(); static const uint256 zeroid; +static uint256 ignoretxid; +static int32_t ignorevin; bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock); int32_t is_hexstr(char *str,int32_t n); bool myAddtomempool(CTransaction &tx, CValidationState *pstate = NULL, bool fSkipExpiry = false); -int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag); -bool myIsutxo_spentinmempool(uint256 txid,int32_t vout); +int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag); +bool myIsutxo_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout); bool mytxid_inmempool(uint256 txid); int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout); int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t *endianedp); CScript GetScriptForMultisig(int nRequired, const std::vector& keys); -int64_t CCaddress_balance(char *coinaddr); +int64_t CCaddress_balance(char *coinaddr,int32_t CCflag); CPubKey CCtxidaddr(char *txidaddr,uint256 txid); +CPubKey CCCustomtxidaddr(char *txidaddr,uint256 txid,uint8_t taddr,uint8_t prefix,uint8_t prefix2); bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn, CTransaction &txOut, std::vector> &preConditions, std::vector> ¶ms); int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); -uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format); uint256 OracleMerkle(int32_t height,uint256 reforacletxid,char *format,std::vectorpublishers); uint256 OraclesBatontxid(uint256 oracletxid,CPubKey pk); - -//int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs); -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); -int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); - -bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); -CScript EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector origpubkey); -//bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); -uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &evalCodeInOpret, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector &origpubkey); - -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, CScript payload); -CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector voutPubkeys, CScript payload); -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra); - +uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format); +uint8_t DecodeOraclesOpRet(const CScript &scriptPubKey,uint256 &oracletxid,CPubKey &pk,int64_t &num); uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,uint256 &batontxid,CPubKey &pk,std::vector &data); int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen); +//int64_t AddAssetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs); +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible); +int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid); +bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx); +void komodo_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,std::vector payload); +int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t len); +int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex); + +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible); +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets); +CScript EncodeTokenImportOpRet(std::vector origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector> oprets); +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::pair opretWithId); +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector> oprets); +int64_t AddCClibtxfee(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk); +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description); +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector> &oprets); +uint8_t DecodeTokenImportOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector> &oprets); +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector> &oprets); +void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible); +bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys); // CCcustom CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv); +//uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopret1, std::vector &vopret2); // CCutils +bool priv2addr(char *coinaddr,uint8_t buf33[33],uint8_t priv32[32]); CPubKey buf2pk(uint8_t *buf33); void endiancpy(uint8_t *dest,uint8_t *src,int32_t len); uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t entropyvout,int32_t usevout); @@ -189,26 +233,29 @@ CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2); CC* GetCryptoCondition(CScript const& scriptSig); void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); -void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); - +void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2,uint8_t *priv,char *coinaddr); CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2); +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2); CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk); +CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk); +CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2); CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2); CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk); - +CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk); bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk); bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2); void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr); int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode); bool IsCCInput(CScript const& scriptSig); +bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime); int32_t unstringbits(char *buf,uint64_t bits); uint64_t stringbits(char *str); uint256 revuint256(uint256 txid); bool pubkey2addr(char *destaddr,uint8_t *pubkey33); char *uint256_str(char *dest,uint256 txid); char *pubkey33_str(char *dest,uint8_t *pubkey33); -uint256 Parseuint256(char *hexstr); +uint256 Parseuint256(const char *hexstr); CPubKey pubkey2pk(std::vector pubkey); int64_t CCfullsupply(uint256 tokenid); int64_t CCtoken_balance(char *destaddr,uint256 tokenid); @@ -219,20 +266,24 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK bool ConstrainVout(CTxOut vout,int32_t CCflag,char *cmpaddr,int64_t nValue); bool PreventCC(Eval* eval,const CTransaction &tx,int32_t preventCCvins,int32_t numvins,int32_t preventCCvouts,int32_t numvouts); bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); +bool GetCustomscriptaddress(char *destaddr,const CScript &scriptPubKey,uint8_t taddr,uint8_t prefix,uint8_t prefix2); std::vector Mypubkey(); bool Myprivkey(uint8_t myprivkey[]); int64_t CCduration(int32_t &numblocks,uint256 txid); +uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid); +int32_t CCCointxidExists(char const *logcategory,uint256 cointxid); +uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids); bool komodo_txnotarizedconfirmed(uint256 txid); CPubKey check_signing_pubkey(CScript scriptSig); // CCtx bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey); extern std::vector NULL_pubkeys; std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector pubkeys = NULL_pubkeys); -void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr); -void SetCCtxids(std::vector > &addressIndex,char *coinaddr); +void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr,bool CCflag = true); +void SetCCtxids(std::vector > &addressIndex,char *coinaddr,bool CCflag = true); 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); +int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCflag); // curve25519 and sha256 bits256 curve25519_shared(bits256 privkey,bits256 otherpub); @@ -242,4 +293,31 @@ void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uin bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen); UniValue ValueFromAmount(const CAmount& amount); + +// bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream: +#define CCLOG_INFO 0 +#define CCLOG_DEBUG1 1 +#define CCLOG_DEBUG2 2 +#define CCLOG_DEBUG3 3 +#define CCLOG_MAXLEVEL 3 +template +void CCLogPrintStream(const char *category, int level, T print_to_stream) +{ + std::ostringstream stream; + print_to_stream(stream); + if (level < 0) + level = 0; + if (level > CCLOG_MAXLEVEL) + level = CCLOG_MAXLEVEL; + for (int i = level; i <= CCLOG_MAXLEVEL; i++) + if( LogAcceptCategory((std::string(category) + std::string("-") + std::to_string(i)).c_str()) || // '-debug=cctokens-0', '-debug=cctokens-1',... + i == 0 && LogAcceptCategory(std::string(category).c_str()) ) { // also supporting '-debug=cctokens' for CCLOG_INFO + LogPrintStr(stream.str()); + break; + } +} +// use: LOGSTREAM("yourcategory", your-debug-level, stream << "some log data" << data2 << data3 << ... << std::endl); +#define LOGSTREAM(category, level, logoperator) CCLogPrintStream( category, level, [=](std::ostringstream &stream) {logoperator;} ) + + #endif diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index d7db32b99..a6ecf7123 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -34,147 +34,13 @@ Yes, this is quite confusing... - In ValudateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits. + In ValidateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits. We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it. ------------------------------ */ -// NOTE: this inital tx won't be used by other contract -// for tokens to be used there should be at least one 't' tx with other contract's custom opret -CScript EncodeTokenCreateOpRet(uint8_t funcid,std::vector origpubkey,std::string name,std::string description) -{ - CScript opret; uint8_t evalcode = EVAL_TOKENS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description); - return(opret); -} - -// this is for other contracts which use tokens and build customized extra payloads to token's opret: -CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, CScript payload) -{ - CScript opret; - uint8_t tokenFuncId = 't'; - uint8_t evalCodeInOpret = EVAL_TOKENS; - - tokenid = revuint256(tokenid); - - uint8_t ccType = 0; - if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) - ccType = voutPubkeys.size(); - - std::vector vpayload; - GetOpReturnData(payload, vpayload); - - opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; \ - if (ccType >= 1) ss << voutPubkeys[0]; \ - if (ccType == 2) ss << voutPubkeys[1]; \ - if (vpayload.size() > 0) ss << vpayload;); - - - // "error 64: scriptpubkey": - // if (payload.size() > 0) - // opret += payload; - - // error 64: scriptpubkey: - // CScript opretPayloadNoOpcode(vpayload); - // return opret + opretPayloadNoOpcode; - - // how to attach payload without re-serialization: - // sig_aborted: - // opret.resize(opret.size() + vpayload.size()); - // CScript::iterator it = opret.begin() + opret.size(); - // for (int i = 0; i < vpayload.size(); i++, it++) - // *it = vpayload[i]; - - return opret; -} - -// overload for compatibility -CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector voutPubkeys, CScript payload) -{ - return EncodeTokenOpRet(tokenid, voutPubkeys, payload); -} - -uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description) -{ - std::vector vopret; uint8_t dummyEvalcode, funcid, *script; - - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( script != 0 && vopret.size() > 2 && script[0] == EVAL_TOKENS && script[1] == 'c' ) - { - if ( E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description) != 0 ) - return(funcid); - } - return (uint8_t)0; -} - -uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCode, uint256 &tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) -{ - std::vector vopret, extra, dummyPubkey; - uint8_t funcId=0, *script, dummyEvalCode, dummyFuncId, ccType; - std::string dummyName; std::string dummyDescription; - CPubKey voutPubkey1, voutPubkey2; - - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - tokenid = zeroid; - - if (script != 0 && vopret.size() > 2) - { - // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set! - bool isEof = true; - - evalCode = script[0]; - if (evalCode != EVAL_TOKENS) - return (uint8_t)0; - - funcId = script[1]; - //fprintf(stderr,"decode.[%c]\n",funcId); - - switch( funcId ) - { - case 'c': - return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription); - //break; - case 't': - //not used yet: case 'l': - // NOTE: 'E_UNMARSHAL result==false' means 'parse error' OR 'not eof state'. Consequently, 'result==false' but 'isEof==true' means just 'parse error' - if (E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; if (ccType >= 1) ss >> voutPubkey1; if (ccType == 2) ss >> voutPubkey2; isEof = ss.eof(); vopretExtra = std::vector(ss.begin(), ss.end())) - || !isEof) - { - - if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType - std::cerr << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; - return (uint8_t)0; - } - - // add verification pubkeys: - voutPubkeys.clear(); - if (voutPubkey1.IsValid()) - voutPubkeys.push_back(voutPubkey1); - if (voutPubkey2.IsValid()) - voutPubkeys.push_back(voutPubkey2); - - tokenid = revuint256(tokenid); - return(funcId); - } - std::cerr << "DecodeTokenOpRet() bad opret format, isEof=" << isEof << " ccType=" << ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl; - return (uint8_t)0; - - default: - std::cerr << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl; - return (uint8_t)0; - } - } - else { - std::cerr << "DecodeTokenOpRet() empty opret, could not parse" << std::endl; - } - return (uint8_t)0; -} - - // tx validation bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) @@ -183,33 +49,37 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2; int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts; int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore; - std::vector vopretExtra, tmporigpubkey, ignorepubkey; + std::vector> oprets; + vscript_t /*vopretExtra,*/ tmporigpubkey, ignorepubkey; uint8_t funcid, evalCodeInOpret; char destaddr[64], origaddr[64], CCaddr[64]; - std::vector voutTokenPubkeys; + std::vector voutTokenPubkeys, vinTokenPubkeys; - //return true; + if (strcmp(ASSETCHAINS_SYMBOL, "ROGUE") == 0 && chainActive.Height() <= 12500) + return true; numvins = tx.vin.size(); numvouts = tx.vout.size(); outputs = inputs = 0; preventCCvins = preventCCvouts = -1; - if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, vopretExtra)) == 0) + // check boundaries: + if (numvouts < 1) + return eval->Invalid("no vouts"); + + if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets)) == 0) return eval->Invalid("TokenValidate: invalid opreturn payload"); - fprintf(stderr, "TokensValidate (%c) evalcode=0x%0x\n", funcid, cp->evalcode); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << std::hex << (int)cp->evalcode << std::endl); - if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) + if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0) return eval->Invalid("cant find token create txid"); - else if (IsCCInput(tx.vin[0].scriptSig) != 0) - return eval->Invalid("illegal token vin0"); - else if (numvouts < 1) - return eval->Invalid("no vouts"); + //else if (IsCCInput(tx.vin[0].scriptSig) != 0) + // return eval->Invalid("illegal token vin0"); // <-- this validation was removed because some token tx might not have normal vins else if (funcid != 'c') { - if (tokenid == zeroid) - return eval->Invalid("illegal tokenid"); + if (tokenid == zeroid) + return eval->Invalid("illegal tokenid"); else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) { if (!eval->Valid()) return false; //TokenExactAmounts must call eval->Invalid()! @@ -218,8 +88,19 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & } } + // validate spending from token cc addr: allowed only for burned non-fungible tokens: + if (ExtractTokensCCVinPubkeys(tx, vinTokenPubkeys) && std::find(vinTokenPubkeys.begin(), vinTokenPubkeys.end(), GetUnspendable(cp, NULL)) != vinTokenPubkeys.end()) { + // validate spending from token unspendable cc addr: + int64_t burnedAmount = HasBurnedTokensvouts(cp, eval, tx, tokenid); + if (burnedAmount > 0) { + vscript_t vopretNonfungible; + GetNonfungibleData(tokenid, vopretNonfungible); + if( vopretNonfungible.empty() ) + return eval->Invalid("spending cc marker not supported for fungible tokens"); + } + } - switch (funcid) + switch (funcid) { case 'c': // create wont be called to be verified as it has no CC inputs //vin.0: normal input @@ -240,11 +121,11 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & if (inputs == 0) return eval->Invalid("no token inputs for transfer"); - fprintf(stderr, "token transfer preliminarily validated %.8f -> %.8f (%d %d)\n", (double)inputs / COIN, (double)outputs / COIN, preventCCvins, preventCCvouts); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "token transfer preliminarily validated inputs=" << inputs << "->outputs=" << outputs << " preventCCvins=" << preventCCvins<< " preventCCvouts=" << preventCCvouts << std::endl); break; // breaking to other contract validation... default: - fprintf(stderr, "illegal tokens funcid.(%c)\n", funcid); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "illegal tokens funcid=" << (char)(funcid?funcid:' ') << std::endl); return eval->Invalid("unexpected token funcid"); } @@ -266,22 +147,24 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction & // helper funcs: -// extract my vins pubkeys: -bool ExtractTokensVinPubkeys(CTransaction tx, std::vector &vinPubkeys) { +// extract cc token vins' pubkeys: +bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector &vinPubkeys) { bool found = false; CPubKey pubkey; struct CCcontract_info *cpTokens, tokensC; cpTokens = CCinit(&tokensC, EVAL_TOKENS); + vinPubkeys.clear(); for (int32_t i = 0; i < tx.vin.size(); i++) - { // check for additional contracts which may send tokens to the Tokens contract + { + // check for cc token vins: if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) { auto findEval = [](CC *cond, struct CCVisitor _) { - bool r = false; //cc_typeId(cond) == CC_Eval && cond->codeLength == 1 && cond->code[0] == EVAL_TOKENS; + bool r = false; if (cc_typeId(cond) == CC_Secp256k1) { *(CPubKey*)_.context = buf2pk(cond->publicKey); @@ -313,65 +196,113 @@ bool ExtractTokensVinPubkeys(CTransaction tx, std::vector &vinPubkeys) thread_local uint32_t tokenValIndentSize = 0; // validates opret for token tx: -uint8_t ValidateTokenOpret(CTransaction tx, int32_t v, uint256 tokenid, std::vector &voutPubkeys, std::vector &vopretExtra) { +uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) { - uint256 tokenidOpret, tokenidOpret2; + uint256 tokenidOpret = zeroid; uint8_t funcid; uint8_t dummyEvalCode; + std::vector voutPubkeysDummy; + std::vector> opretsDummy; // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); - int32_t n = tx.vout.size(); + if (tx.vout.size() == 0) + return (uint8_t)0; - if ((funcid = DecodeTokenOpRet(tx.vout[n - 1].scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeys, vopretExtra)) == 0) + if ((funcid = DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeysDummy, opretsDummy)) == 0) { - std::cerr << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl; - return(false); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl); + return (uint8_t)0; } else if (funcid == 'c') { - if (tokenid != zeroid && tokenid == tx.GetHash() && v == 0) { - //std::cerr << indentStr << "ValidateTokenOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; + if (tokenid != zeroid && tokenid == tx.GetHash()) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); return funcid; } + else { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenbase txid=" << tx.GetHash().GetHex() << std::endl); + } } + else if (funcid == 'i') + { + if (tokenid != zeroid && tokenid == tx.GetHash()) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is import 'i' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); + return funcid; + } + else { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my import txid=" << tx.GetHash().GetHex() << std::endl); + } + } else if (funcid == 't') { //std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; if (tokenid != zeroid && tokenid == tokenidOpret) { - //std::cerr << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl; + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl); return funcid; } + else { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenid=" << tokenidOpret.GetHex() << std::endl); + } } - //std::cerr << indentStr << "ValidateTokenOpret() return false funcid=" << (char)funcid << " tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl; + else { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl); + } return (uint8_t)0; } +// remove token->unspendablePk (it is only for marker usage) +void FilterOutTokensUnspendablePk(const std::vector &sourcePubkeys, std::vector &destPubkeys) { + struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + CPubKey tokensUnspendablePk = GetUnspendable(cpTokens, NULL); + destPubkeys.clear(); + + for (auto pk : sourcePubkeys) + if (pk != tokensUnspendablePk) + destPubkeys.push_back(pk); + +} + +void FilterOutNonCCOprets(const std::vector> &oprets, vscript_t &vopret) { + + vopret.clear(); + + if (oprets.size() > 2) + LOGSTREAM("cctokens", CCLOG_INFO, stream << "FilterOutNonCCOprets() warning!! oprets.size > 2 currently not supported" << oprets.size() << std::endl); + + for (auto o : oprets) { + if (o.first < OPRETID_FIRSTNONCCDATA) { // skip burn, import, etc opret data + vopret = o.second; // return first contract opret (more than 1 is not supported yet) + break; + } + } +} + // Checks if the vout is a really Tokens CC vout // also checks tokenid in opret or txid if this is 'c' tx // goDeeper is true: the func also validates amounts of the passed transaction: // it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx // checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true! -int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) +int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid) { // this is just for log messages indentation fur debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); - //std::cerr << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl; + + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl); - //TODO: validate cc vouts are EVAL_TOKENS! - if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here + int32_t n = tx.vout.size(); + // just check boundaries: + if (n == 0 || v < 0 || v >= n-1) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n-1)" << " v=" << v << " n=" << n << " returning 0" << std::endl); + return(0); + } + + if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition()) { - int32_t n = tx.vout.size(); - // just check boundaries: - if (v >= n - 1) { // just moved this up (dimxy) - std::cerr << indentStr << "isTokensvout() internal err: (v >= n - 1), returning 0" << std::endl; - return(0); - } - if (goDeeper) { - //std::cerr << indentStr << "IsTokensvout() maxTokenExactAmountDepth=" << maxTokenExactAmountDepth << std::endl; //validate all tx int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0; @@ -384,100 +315,142 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c // if ccInputs != ccOutputs and it is not the tokenbase tx // this means it is possibly a fake tx (dimxy): if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy) - std::cerr << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl); return 0; } } } - // moved opret checking to this new reusable func (dimxy): - std::vector voutPubkeys; - std::vector vopretExtra; - const uint8_t funcId = ValidateTokenOpret(tx, v, reftokenid, voutPubkeys, vopretExtra); + // token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'): + const uint8_t funcId = ValidateTokenOpret(tx, reftokenid); //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - if (funcId != 0) { - //std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; + if (funcId != 0) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); - if (checkPubkeys && funcId != 'c') { // verify that the vout is token's (for 'c' there is no pubkeys!): + uint8_t dummyEvalCode; + uint256 tokenIdOpret; + std::vector voutPubkeys, voutPubkeysInOpret; + vscript_t vopretExtra, vopretNonfungible; + std::vector> oprets; - //std::cerr << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl; - - uint8_t evalCodeInOpret; - if (vopretExtra.size() >= 2 /*|| vopretExtra.size() != vopretExtra.begin()[0] <-- shold we check this?*/) { - evalCodeInOpret = vopretExtra.begin()[1]; - } - else { - // if payload is empty maybe it is a claim to non-payload-one-token-eval vout? - evalCodeInOpret = EVAL_TOKENS; - } + uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim + uint8_t evalCode2 = 0; // will be checked if zero or not + + // test vouts for possible token use-cases: + std::vector> testVouts; + + DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysInOpret, oprets); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() oprets.size()=" << oprets.size() << std::endl); + + // get assets/channels/gateways token data: + FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "IsTokensvout() vopretExtra=" << HexStr(vopretExtra) << std::endl); + + // get non-fungible data + GetNonfungibleData(reftokenid, vopretNonfungible); + FilterOutTokensUnspendablePk(voutPubkeysInOpret, voutPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there) + + // NOTE: evalcode order in vouts is important: + // non-fungible-eval -> EVAL_TOKENS -> assets-eval + + if (vopretNonfungible.size() > 0) + evalCode = vopretNonfungible.begin()[0]; + if (vopretExtra.size() > 0) + evalCode2 = vopretExtra.begin()[0]; + + if (evalCode == EVAL_TOKENS && evalCode2 != 0) { + evalCode = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...) + evalCode2 = 0; + } + + if( /*checkPubkeys &&*/ funcId != 'c' ) { // for 'c' there is no pubkeys + // verify that the vout is token by constructing vouts with the pubkeys in the opret: // maybe this is dual-eval 1 pubkey or 1of2 pubkey vout? if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) { - CTxOut testDualVout; - // check dual-eval 1 pubkey vout with the first pubkey - testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0]); - if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=0), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } - - if(voutPubkeys.size() == 2) { - // check dual eval 1of2 pubkeys vout - testDualVout = MakeTokensCC1of2vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]); - if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token 1of2 vout, eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + // check dual/three-eval 1 pubkey vout with the first pubkey + testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) ); + if (evalCode2 != 0) + // also check in backward evalcode order + testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) ); - // check dual eval 1 pubkey vout with the second pubkey - testDualVout = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, voutPubkeys[1]); - if (tx.vout[v].scriptPubKey == testDualVout.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token vout (i=1), eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + if(voutPubkeys.size() == 2) { + // check dual/three eval 1of2 pubkeys vout + testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) ); + // check dual/three eval 1 pubkey vout with the second pubkey + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]"))); + if (evalCode2 != 0) { + // also check in backward evalcode order: + // check dual/three eval 1of2 pubkeys vout + testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2 backward-eval"))); + // check dual/three eval 1 pubkey vout with the second pubkey + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval"))); + } } - // maybe this is claim to single-eval token? - CTxOut testTokenVout1; - testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]); - if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=0), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + // maybe this is like gatewayclaim to single-eval token? + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]"))); + // maybe this is like FillSell for non-fungible token? + if( evalCode != 0 ) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval-token cc1 pk[0]"))); + if( evalCode2 != 0 ) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval2-token cc1 pk[0]"))); if (voutPubkeys.size() == 2) { - testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]); - if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is single-eval token vout (i=1), returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + // the same for pk[1]: + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]"))); + if (evalCode != 0) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]"))); + if (evalCode2 != 0) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval2-token cc1 pk[1]"))); } } - // maybe it is single-eval or dual-eval token change? - std::vector vinPubkeys; - ExtractTokensVinPubkeys(tx, vinPubkeys); + // maybe it is single-eval or dual/three-eval token change? + std::vector vinPubkeys, vinPubkeysUnfiltered; + ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered); + FilterOutTokensUnspendablePk(vinPubkeysUnfiltered, vinPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there) for(std::vector::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) { - CTxOut testTokenVout1 = MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it); - CTxOut testDualVout1 = MakeTokensCC1vout(evalCodeInOpret, tx.vout[v].nValue, *it); + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk"))); + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk"))); - if (tx.vout[v].scriptPubKey == testTokenVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is single-eval token change, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } - - if (tx.vout[v].scriptPubKey == testDualVout1.scriptPubKey) { - //std::cerr << indentStr << "IsTokensvout() this is dual-eval token change, vout eval2=" << (int)evalCodeInOpret << ", returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; - } + if (evalCode2 != 0) + // also check in backward evalcode order: + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval"))); } + } else { - //std::cerr << indentStr << "IsTokensvout() returns without pubkey check value=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl; - return tx.vout[v].nValue; + CPubKey origPubkey; + vscript_t vorigPubkey; + std::string dummyName, dummyDescription; + std::vector> oprets; + + if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + return 0; + } + + origPubkey = pubkey2pk(vorigPubkey); + + // for 'c' recognize the tokens only to token originator pubkey (but not to unspendable <-- closed sec violation) + // maybe this is like gatewayclaim to single-eval token? + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk"))); + // maybe this is like FillSell for non-fungible token? + if (evalCode != 0) + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); } + + // try all test vouts: + for (auto t : testVouts) { + if (t.first == tx.vout[v]) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); + return tx.vout[v].nValue; + } + } + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); } //std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str()); @@ -487,7 +460,7 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *c } // compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs) -bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid) +bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid) { CTransaction vinTx; uint256 hashBlock; @@ -503,6 +476,8 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu // this is just for log messages indentation for debugging recursive calls: std::string indentStr = std::string().append(tokenValIndentSize, '.'); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + for (int32_t i = 0; iismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/) @@ -511,37 +486,38 @@ bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inpu // we are not inside the validation code -- dimxy if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock))) { - std::cerr << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl); return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt"); } else { - tokenValIndentSize++; - // validate vouts of vintx - //std::cerr << indentStr << "TokenExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl; - tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, tokenid); + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl); + + // validate vouts of vintx + tokenValIndentSize++; + tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, reftokenid); tokenValIndentSize--; if (tokenoshis != 0) { - std::cerr << indentStr << "TokensExactAmounts() vin i=" << i << " tokenoshis=" << tokenoshis << std::endl; + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl); inputs += tokenoshis; } } } } - - for (int32_t i = 0; iInvalid() here! } else return true; } -// add inputs from token cc addr -int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) + +// get non-fungible data from 'tokenbase' tx (the data might be empty) +void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible) +{ + CTransaction tokenbasetx; + uint256 hashBlock; + + if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "GetNonfungibleData() cound not load token creation tx=" << tokenid.GetHex() << std::endl); + return; + } + + vopretNonfungible.clear(); + // check if it is non-fungible tx and get its second evalcode from non-fungible payload + if (tokenbasetx.vout.size() > 0) { + std::vector origpubkey; + std::string name, description; + std::vector> oprets; + + if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + } + } +} + + +// overload, adds inputs from token cc addr +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) { + vscript_t vopretNonfungibleDummy; + return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, vopretNonfungibleDummy); +} + +// adds inputs from token cc addr and returns non-fungible opret payload if present +// also sets evalcode in cp, if needed +int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible) { char tokenaddr[64], destaddr[64]; - int64_t threshold, nValue, price, totalinputs = 0; - uint256 txid, hashBlock; - //std::vector vopretExtra; - CTransaction vintx; - int32_t j, vout, n = 0; + int64_t threshold, nValue, price, totalinputs = 0; + int32_t n = 0; std::vector > unspentOutputs; - GetTokensCCaddress(cp, tokenaddr, pk); - SetCCunspents(unspentOutputs, tokenaddr); + GetNonfungibleData(tokenid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cp->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; - threshold = total / (maxinputs != 0 ? maxinputs : 64); // TODO: is maxinputs really could not be over 64? what if i want to calc total balance? + GetTokensCCaddress(cp, tokenaddr, pk); + SetCCunspents(unspentOutputs, tokenaddr,true); + + if (unspentOutputs.empty()) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl); + } + + threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS); 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; - for (j = 0; jfirst.txhash; + int32_t vout = (int32_t)it->first.index; + + if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue) continue; - if (GetTransaction(txid, vintx, hashBlock, false) != 0) + int32_t ivin; + for (ivin = 0; ivin < mtx.vin.size(); ivin ++) + if (vintxid == mtx.vin[ivin].prevout.hash && vout == mtx.vin[ivin].prevout.n) + break; + if (ivin != mtx.vin.size()) // that is, the tx.vout is already added to mtx.vin (in some previous calls) + continue; + + if (GetTransaction(vintxid, vintx, hashBlock, false) != 0) { Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey); - if (strcmp(destaddr, tokenaddr) != 0 && strcmp(destaddr, cp->unspendableCCaddr) != 0 && strcmp(destaddr, cp->unspendableaddr2) != 0) + if (strcmp(destaddr, tokenaddr) != 0 && + strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used? + strcmp(destaddr, cp->unspendableaddr2) != 0) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)? continue; - //fprintf(stderr, "AddTokenCCInputs() check destaddress=%s vout amount=%.8f\n", destaddr, (double)vintx.vout[vout].nValue / COIN); - - std::vector vinPubkeys; - if ((nValue = IsTokensvout(true, true/*<--add only checked token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(txid, vout) == 0) + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl); + + if ((nValue = IsTokensvout(true, true/*<--add only valid token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,vintxid, vout) == 0) { - if (total != 0 && maxinputs != 0) - mtx.vin.push_back(CTxIn(txid, vout, CScript())); + //for non-fungible tokens check payload: + if (!vopretNonfungible.empty()) { + vscript_t vopret; + + // check if it is non-fungible token: + GetNonfungibleData(tokenid, vopret); + if (vopret != vopretNonfungible) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl); + continue; + } + // non-fungible evalCode2 cc contract should also check if there exists only one non-fungible vout with amount = 1 + } + + + if (total != 0 && maxinputs != 0) // if it is not just to calc amount... + mtx.vin.push_back(CTxIn(vintxid, vout, CScript())); + nValue = it->second.satoshis; totalinputs += nValue; - std::cerr << "AddTokenInputs() adding input nValue=" << nValue << std::endl; + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl); n++; + if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; } } } - //std::cerr << "AddTokenInputs() found totalinputs=" << totalinputs << std::endl; + //std::cerr << "AddTokenCCInputs() found totalinputs=" << totalinputs << std::endl; return(totalinputs); } +// checks if any token vouts are sent to 'dead' pubkey +int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid) +{ + uint8_t dummyEvalCode; + uint256 tokenIdOpret; + std::vector voutPubkeys, voutPubkeysDummy; + std::vector> oprets; + vscript_t vopretExtra, vopretNonfungible; -std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description) + uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim + uint8_t evalCode2 = 0; // will be checked if zero or not + + // test vouts for possible token use-cases: + std::vector> testVouts; + + int32_t n = tx.vout.size(); + // just check boundaries: + if (n == 0) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() incorrect params: tx.vout.size() == 0, txid=" << tx.GetHash().GetHex() << std::endl); + return(0); + } + + + if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenIdOpret, voutPubkeysDummy, oprets) == 0) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "HasBurnedTokensvouts() cannot parse opret DecodeTokenOpRet returned 0, txid=" << tx.GetHash().GetHex() << std::endl); + return 0; + } + + // get assets/channels/gateways token data: + FilterOutNonCCOprets(oprets, vopretExtra); // NOTE: only 1 additional evalcode in token opret is currently supported + + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() vopretExtra=" << HexStr(vopretExtra) << std::endl); + + GetNonfungibleData(reftokenid, vopretNonfungible); + + if (vopretNonfungible.size() > 0) + evalCode = vopretNonfungible.begin()[0]; + if (vopretExtra.size() > 0) + evalCode2 = vopretExtra.begin()[0]; + + if (evalCode == EVAL_TOKENS && evalCode2 != 0) { + evalCode = evalCode2; + evalCode2 = 0; + } + + voutPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); + + int64_t burnedAmount = 0; + + for (int i = 0; i < tx.vout.size(); i++) { + + if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition()) + { + // make all possible token vouts for dead pk: + for (std::vector::iterator it = voutPubkeys.begin(); it != voutPubkeys.end(); it++) { + testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[i].nValue, *it), std::string("single-eval cc1 burn pk"))); + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk"))); + + if (evalCode2 != 0) + // also check in backward evalcode order: + testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[i].nValue, *it), std::string("three-eval cc1 burn pk backward-eval"))); + } + + // try all test vouts: + for (auto t : testVouts) { + if (t.first == tx.vout[i]) { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "HasBurnedTokensvouts() burned amount=" << tx.vout[i].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl); + burnedAmount += tx.vout[i].nValue; + } + } + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "HasBurnedTokensvouts() total burned=" << burnedAmount << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl); + } + } + + return burnedAmount; +} + +CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) { + + uint8_t funcId, evalCode; + uint256 tokenid; + std::vector voutTokenPubkeys; + std::vector> oprets; + + if ((funcId = DecodeTokenOpRet(scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets)) != 0) { + CTransaction tokenbasetx; + uint256 hashBlock; + + if (myGetTransaction(tokenid, tokenbasetx, hashBlock) && tokenbasetx.vout.size() > 0) { + vscript_t vorigpubkey; + std::string name, desc; + if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, desc) != 0) + return pubkey2pk(vorigpubkey); + } + } + return CPubKey(); //return invalid pubkey +} + + +std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey mypk; struct CCcontract_info *cp, C; - if (assetsupply < 0) - { - fprintf(stderr, "negative assetsupply %lld\n", (long long)assetsupply); - return(""); + if (tokensupply < 0) { + CCerror = "negative tokensupply"; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() =" << CCerror << "=" << tokensupply << std::endl); + return std::string(""); } + if (!nonfungibleData.empty() && tokensupply != 1) { + CCerror = "for non-fungible tokens tokensupply should be equal to 1"; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl); + return std::string(""); + } + cp = CCinit(&C, EVAL_TOKENS); - if (name.size() > 32 || description.size() > 4096) + if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level { - fprintf(stderr, "name.%d or description.%d is too big\n", (int32_t)name.size(), (int32_t)description.size()); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl); + CCerror = "name should be <= 32, description should be <= 4096"; return(""); } if (txfee == 0) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - if (AddNormalinputs(mtx, mypk, assetsupply + 2 * txfee, 64) > 0) + if (AddNormalinputs(mtx, mypk, tokensupply + 2 * txfee, 64) > 0) { - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, assetsupply, mypk)); - mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); - return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description))); + uint8_t destEvalCode = EVAL_TOKENS; + if( nonfungibleData.size() > 0 ) + destEvalCode = nonfungibleData.begin()[0]; + + // NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout(): + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to token cc addr, burnable and validated, vout pos now changed to 0 (from 1) + mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk)); + //mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); // old marker (non-burnable because spending could not be validated) + //mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // ...moved to vout=0 for matching with rogue-game token + + return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData))); } - return(""); + + CCerror = "cant find normal inputs"; + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl); + return std::string(""); } - -std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector destpubkey, int64_t total) +// transfer tokens to another pubkey +// param additionalEvalCode allows transfer of dual-eval non-fungible tokens +std::string TokenTransfer(int64_t txfee, uint256 tokenid, vscript_t destpubkey, int64_t total) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C; - std::vector emptyExtraOpret; + vscript_t vopretNonfungible, vopretEmpty; - if (total < 0) - { - fprintf(stderr, "negative total %lld\n", (long long)total); + if (total < 0) { + CCerror = strprintf("negative total"); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << CCerror << "=" << total << std::endl); return(""); } + cp = CCinit(&C, EVAL_TOKENS); + if (txfee == 0) txfee = 10000; mypk = pubkey2pk(Mypubkey()); if (AddNormalinputs(mtx, mypk, txfee, 3) > 0) { - //n = outputs.size(); - //if ( n == amounts.size() ) - //{ - // for (i=0; i 0) + mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore + + if ((inputs = AddTokenCCInputs(cp, mtx, mypk, tokenid, total, 60, vopretNonfungible)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx! { - if (inputs < total) { //added dimxy - std::cerr << "AssetTransfer(): insufficient funds" << std::endl; - return (""); + CCerror = strprintf("insufficient token inputs"); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl); + return std::string(""); } + + uint8_t destEvalCode = EVAL_TOKENS; + if (vopretNonfungible.size() > 0) + destEvalCode = vopretNonfungible.begin()[0]; + if (inputs > total) CCchange = (inputs - total); - //for (i=0; i voutTokenPubkeys; voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout - return(FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet('t', EVAL_TOKENS, assetid, voutTokenPubkeys, CScript()))); // By setting EVAL_TOKENS we're getting out from assets validation code + return FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair((uint8_t)0, vopretEmpty))); } else { - fprintf(stderr, "not enough CC token inputs for %.8f\n", (double)total / COIN); + CCerror = strprintf("no token inputs"); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << total << std::endl); } //} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size()); } else { - fprintf(stderr, "not enough normal inputs for txfee\n"); + CCerror = strprintf("insufficient normal inputs for tx fee"); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl); } return(""); } @@ -708,7 +866,7 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid) if (GetTransaction(tokenid, tokentx, hashBlock, false) == 0) { - fprintf(stderr, "cant find tokenid\n"); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "cant find tokenid" << std::endl); CCerror = strprintf("cant find tokenid"); return 0; } @@ -720,49 +878,79 @@ int64_t GetTokenBalance(CPubKey pk, uint256 tokenid) UniValue TokenInfo(uint256 tokenid) { - UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; std::vector origpubkey; std::string name, description; char str[67], numstr[65]; - if (GetTransaction(tokenid, vintx, hashBlock, false) == 0) + UniValue result(UniValue::VOBJ); + uint256 hashBlock; + CTransaction vintx; + std::vector origpubkey; + std::vector> oprets; + vscript_t vopretNonfungible; + std::string name, description; + struct CCcontract_info *cpTokens, tokensCCinfo; + + cpTokens = CCinit(&tokensCCinfo, EVAL_TOKENS); + + if( !GetTransaction(tokenid, vintx, hashBlock, false) ) { - fprintf(stderr, "cant find assetid\n"); + fprintf(stderr, "TokenInfo() cant find tokenid\n"); result.push_back(Pair("result", "error")); result.push_back(Pair("error", "cant find tokenid")); return(result); } - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) == 0) + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c') { - fprintf(stderr, "assetid isnt token creation txid\n"); + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl); result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "assetid isnt token creation txid")); + result.push_back(Pair("error", "tokenid isnt token creation txid")); + return result; } result.push_back(Pair("result", "success")); - result.push_back(Pair("tokenid", uint256_str(str, tokenid))); - result.push_back(Pair("owner", pubkey33_str(str, origpubkey.data()))); + result.push_back(Pair("tokenid", tokenid.GetHex())); + result.push_back(Pair("owner", HexStr(origpubkey))); result.push_back(Pair("name", name)); - result.push_back(Pair("supply", vintx.vout[0].nValue)); + + int64_t supply = 0, output; + for (int v = 0; v < vintx.vout.size() - 1; v++) + if ((output = IsTokensvout(false, true, cpTokens, NULL, vintx, v, tokenid)) > 0) + supply += output; + result.push_back(Pair("supply", supply)); result.push_back(Pair("description", description)); - return(result); + + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + if( !vopretNonfungible.empty() ) + result.push_back(Pair("data", HexStr(vopretNonfungible))); + + return result; } UniValue TokenList() { UniValue result(UniValue::VARR); std::vector > addressIndex; + std::vector > addressIndexCCMarker; + struct CCcontract_info *cp, C; uint256 txid, hashBlock; CTransaction vintx; std::vector origpubkey; - std::string name, description; char str[65]; + std::string name, description; cp = CCinit(&C, EVAL_TOKENS); - SetCCtxids(addressIndex, cp->normaladdr); - for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) - { - txid = it->first.txhash; - if (GetTransaction(txid, vintx, hashBlock, false) != 0) - { - if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) - { - result.push_back(uint256_str(str, txid)); - } - } + + auto addTokenId = [&](uint256 txid) { + if (GetTransaction(txid, vintx, hashBlock, false) != 0) { + if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description) != 0) { + result.push_back(txid.GetHex()); + } + } + }; + + SetCCtxids(addressIndex, cp->normaladdr,false); // find by old normal addr marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) { + addTokenId(it->first.txhash); } + + SetCCunspents(addressIndexCCMarker, cp->unspendableCCaddr,true); // find by burnable validated cc addr marker + for (std::vector >::const_iterator it = addressIndexCCMarker.begin(); it != addressIndexCCMarker.end(); it++) { + addTokenId(it->first.txhash); + } + return(result); } diff --git a/src/cc/CCtokens.h b/src/cc/CCtokens.h index e7bb62101..f63c563a9 100644 --- a/src/cc/CCtokens.h +++ b/src/cc/CCtokens.h @@ -28,17 +28,13 @@ // CCcustom bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid); -//int64_t IsTokensvout(bool goDeeper, bool checkPubkeys, struct CCcontract_info *cp, Eval* eval, std::vector &origpubkey, const CTransaction& tx, int32_t v, uint256 reftokenid, std::vector vinPubkeys); -std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description); +std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description, std::vector nonfungibleData); std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector destpubkey, int64_t total); +int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid); +CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey); int64_t GetTokenBalance(CPubKey pk, uint256 tokenid); UniValue TokenInfo(uint256 tokenid); UniValue TokenList(); -//this is in CCinclude.h int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs); - -//this is in CCinclude.h uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey,std::vector &origpubkey,std::string &name,std::string &description); - - #endif diff --git a/src/cc/CCtokensOpRet.cpp b/src/cc/CCtokensOpRet.cpp new file mode 100644 index 000000000..4c6dc4b6d --- /dev/null +++ b/src/cc/CCtokensOpRet.cpp @@ -0,0 +1,296 @@ +// encode decode tokens opret +// (moved to a separate file to enable linking lib common.so with importcoin.cpp) + +#include "CCtokens.h" + +#ifndef IS_CHARINSTR +#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) +#endif + +// NOTE: this inital tx won't be used by other contract +// for tokens to be used there should be at least one 't' tx with other contract's custom opret +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, vscript_t vopretNonfungible) +{ + /* CScript opret; + uint8_t evalcode = EVAL_TOKENS; + funcid = 'c'; // override the param + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; \ + if (!vopretNonfungible.empty()) { + ss << (uint8_t)OPRETID_NONFUNGIBLEDATA; + ss << vopretNonfungible; + }); */ + + std::vector> oprets; + + if(!vopretNonfungible.empty()) + oprets.push_back(std::make_pair(OPRETID_NONFUNGIBLEDATA, vopretNonfungible)); + return EncodeTokenCreateOpRet(funcid, origpubkey, name, description, oprets); +} + +CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector origpubkey, std::string name, std::string description, std::vector> oprets) +{ + CScript opret; + uint8_t evalcode = EVAL_TOKENS; + funcid = 'c'; // override the param + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } + }); + return(opret); +} + +// opret 'i' for imported tokens +CScript EncodeTokenImportOpRet(std::vector origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector> oprets) +{ + CScript opret; + uint8_t evalcode = EVAL_TOKENS; + uint8_t funcid = 'i'; + + srctokenid = revuint256(srctokenid); // do not forget this + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description << srctokenid; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } + }); + return(opret); +} + + + +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::pair opretWithId) +{ + std::vector> oprets; + oprets.push_back(opretWithId); + return EncodeTokenOpRet(tokenid, voutPubkeys, oprets); +} + +CScript EncodeTokenOpRet(uint256 tokenid, std::vector voutPubkeys, std::vector> oprets) +{ + CScript opret; + uint8_t tokenFuncId = 't'; + uint8_t evalCodeInOpret = EVAL_TOKENS; + + tokenid = revuint256(tokenid); + + uint8_t ccType = 0; + if (voutPubkeys.size() >= 0 && voutPubkeys.size() <= 2) + ccType = voutPubkeys.size(); + else { + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "EncodeTokenOpRet voutPubkeys.size()=" << voutPubkeys.size() << " not supported" << std::endl); + } + + //vopret_t vpayload; + //GetOpReturnData(payload, vpayload); + + opret << OP_RETURN << E_MARSHAL(ss << evalCodeInOpret << tokenFuncId << tokenid << ccType; + if (ccType >= 1) ss << voutPubkeys[0]; + if (ccType == 2) ss << voutPubkeys[1]; + for (auto o : oprets) { + if (o.first != 0) { + ss << (uint8_t)o.first; + ss << o.second; + } + }); + + // bad opret cases (tries to attach payload without re-serialization): + + // error "64: scriptpubkey": + // if (payload.size() > 0) + // opret += payload; + + // error "64: scriptpubkey": + // CScript opretPayloadNoOpcode(vpayload); + // return opret + opretPayloadNoOpcode; + + // error "sig_aborted": + // opret.resize(opret.size() + vpayload.size()); + // CScript::iterator it = opret.begin() + opret.size(); + // for (int i = 0; i < vpayload.size(); i++, it++) + // *it = vpayload[i]; + + return opret; +} + +// overload for compatibility +//CScript EncodeTokenOpRet(uint8_t tokenFuncId, uint8_t evalCodeInOpret, uint256 tokenid, std::vector voutPubkeys, CScript payload) +//{ +// return EncodeTokenOpRet(tokenid, voutPubkeys, payload); +//} + +// overload for fungible tokens (no additional data in opret): +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description) { + //vopret_t vopretNonfungibleDummy; + std::vector> opretsDummy; + return DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, opretsDummy); +} + +uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, std::vector> &oprets) +{ + vscript_t vopret, vblob; + uint8_t dummyEvalcode, funcid, opretId = 0; + + GetOpReturnData(scriptPubKey, vopret); + oprets.clear(); + + if (vopret.size() > 2 && vopret.begin()[0] == EVAL_TOKENS && vopret.begin()[1] == 'c') + { + if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; + while (!ss.eof()) { + ss >> opretId; + if (!ss.eof()) { + ss >> vblob; + oprets.push_back(std::make_pair(opretId, vblob)); + } + })) + { + return(funcid); + } + } + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenCreateOpRet() incorrect token create opret" << std::endl); + return (uint8_t)0; +} + +// for imported tokens +uint8_t DecodeTokenImportOpRet(const CScript &scriptPubKey, std::vector &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector> &oprets) +{ + vscript_t vopret, vblob; + uint8_t dummyEvalcode, funcid, opretId = 0; + + GetOpReturnData(scriptPubKey, vopret); + oprets.clear(); + + if (vopret.size() > 2 && vopret.begin()[0] == EVAL_TOKENS && vopret.begin()[1] == 'i') + { + if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; ss >> srctokenid; + while (!ss.eof()) { + ss >> opretId; + if (!ss.eof()) { + ss >> vblob; + oprets.push_back(std::make_pair(opretId, vblob)); + } + })) + { + srctokenid = revuint256(srctokenid); // do not forget this + return(funcid); + } + } + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenImportOpRet() incorrect token import opret" << std::endl); + return (uint8_t)0; +} + +// decodes token opret: +// for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets'). +// for 'c' and 'i' returns only funcid. NOTE: nonfungible data is not returned +uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector &voutPubkeys, std::vector> &oprets) +{ + vscript_t vopret, vblob, dummyPubkey, vnonfungibleDummy; + uint8_t funcId = 0, *script, dummyEvalCode, dummyFuncId, ccType, opretId = 0; + std::string dummyName; std::string dummyDescription; + uint256 dummySrcTokenId; + CPubKey voutPubkey1, voutPubkey2; + + vscript_t voldstyledata; + bool foundOldstyle = false; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + tokenid = zeroid; + oprets.clear(); + + if (script != NULL && vopret.size() > 2) + { + // NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set! + // bool isEof = true; + + evalCodeTokens = script[0]; + if (evalCodeTokens != EVAL_TOKENS) { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect evalcode in tokens opret" << std::endl); + return (uint8_t)0; + } + + funcId = script[1]; + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl); + + switch (funcId) + { + case 'c': + return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, oprets); + case 'i': + return DecodeTokenImportOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, dummySrcTokenId, oprets); + //break; + case 't': + + // compatibility with old-style rogue or assets data (with no opretid): + // try to unmarshal old-style rogue or assets data: + foundOldstyle = E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; + if (ccType >= 1) ss >> voutPubkey1; + if (ccType == 2) ss >> voutPubkey2; + if (!ss.eof()) { + ss >> voldstyledata; + }) && voldstyledata.size() >= 2 && + (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/ && IS_CHARINSTR(voldstyledata.begin()[1], "RHQKG") || + voldstyledata.begin()[0] == EVAL_ASSETS && IS_CHARINSTR(voldstyledata.begin()[1], "sbSBxo")) ; + + if (foundOldstyle || // fix for compatibility with old style data (no opretid) + E_UNMARSHAL(vopret, ss >> dummyEvalCode; ss >> dummyFuncId; ss >> tokenid; ss >> ccType; + if (ccType >= 1) ss >> voutPubkey1; + if (ccType == 2) ss >> voutPubkey2; + while (!ss.eof()) { + ss >> opretId; + if (!ss.eof()) { + ss >> vblob; + oprets.push_back(std::make_pair(opretId, vblob)); + } + })) + { + if (!(ccType >= 0 && ccType <= 2)) { //incorrect ccType + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); + return (uint8_t)0; + } + + // add verification pubkeys: + voutPubkeys.clear(); + if (voutPubkey1.IsValid()) + voutPubkeys.push_back(voutPubkey1); + if (voutPubkey2.IsValid()) + voutPubkeys.push_back(voutPubkey2); + + tokenid = revuint256(tokenid); + + if (foundOldstyle) { //patch for old-style opret data with no opretid + LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "DecodeTokenOpRet() found old-style rogue/asset data, evalcode=" << (int)voldstyledata.begin()[0] << " funcid=" << (char)voldstyledata.begin()[1] << " for tokenid=" << revuint256(tokenid).GetHex() << std::endl); + uint8_t opretIdRestored; + if (voldstyledata.begin()[0] == 0x11 /*EVAL_ROGUE*/) + opretIdRestored = OPRETID_ROGUEGAMEDATA; + else // EVAL_ASSETS + opretIdRestored = OPRETID_ASSETSDATA; + + oprets.push_back(std::make_pair(opretIdRestored, voldstyledata)); + } + + return(funcId); + } + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() bad opret format," << " ccType=" << (int)ccType << " tokenid=" << revuint256(tokenid).GetHex() << std::endl); + return (uint8_t)0; + + default: + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() illegal funcid=" << (int)funcId << std::endl); + return (uint8_t)0; + } + } + else { + LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() empty opret, could not parse" << std::endl); + } + return (uint8_t)0; +} + + + diff --git a/src/cc/CCtx.cpp b/src/cc/CCtx.cpp index ae7483f4d..7435defbf 100644 --- a/src/cc/CCtx.cpp +++ b/src/cc/CCtx.cpp @@ -46,9 +46,9 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0; int64_t utxovalues[CC_MAXVINS],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0; int32_t i,flag,utxovout,n,err = 0; - char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], tokensunspendable[64]; - uint8_t *privkey, myprivkey[32], unspendablepriv[32], tokensunspendablepriv[32], *msg32 = 0; - CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond, *mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; + char myaddr[64], destaddr[64], unspendable[64], mytokensaddr[64], mysingletokensaddr[64], unspendabletokensaddr[64],CC1of2CCaddr[64]; + uint8_t *privkey, myprivkey[32], unspendablepriv[32], /*tokensunspendablepriv[32],*/ *msg32 = 0; + CC *mycond=0, *othercond=0, *othercond2=0,*othercond4=0, *othercond3=0, *othercond1of2=NULL, *othercond1of2tokens = NULL, *cond=0, *condCC2=0,*mytokenscond = NULL, *mysingletokenscond = NULL, *othertokenscond = NULL; CPubKey unspendablepk /*, tokensunspendablepk*/; struct CCcontract_info *cpTokens, tokensC; globalpk = GetUnspendable(cp,0); @@ -69,26 +69,28 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran GetCCaddress(cp,myaddr,mypk); mycond = MakeCCcond1(cp->evalcode,mypk); - // to spend from single-eval evalcode 'unspendable' + // to spend from single-eval evalcode 'unspendable' cc addr unspendablepk = GetUnspendable(cp, unspendablepriv); GetCCaddress(cp, unspendable, unspendablepk); othercond = MakeCCcond1(cp->evalcode, unspendablepk); + GetCCaddress1of2(cp,CC1of2CCaddr,unspendablepk,unspendablepk); + + //fprintf(stderr,"evalcode.%d (%s)\n",cp->evalcode,unspendable); // tokens support: - - // to spend from dual-eval mypk vout + // to spend from dual/three-eval mypk vout GetTokensCCaddress(cp, mytokensaddr, mypk); - mytokenscond = MakeTokensCCcond1(cp->evalcode, mypk); + // NOTE: if additionalEvalcode2 is not set it is a dual-eval (not three-eval) cc cond: + mytokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, mypk); // to spend from single-eval EVAL_TOKENS mypk cpTokens = CCinit(&tokensC, EVAL_TOKENS); GetCCaddress(cpTokens, mysingletokensaddr, mypk); mysingletokenscond = MakeCCcond1(EVAL_TOKENS, mypk); - // to spend from dual-eval EVAL_TOKEN+evalcode 'unspendable' pk - //tokensunspendablepk = GetUnspendable(cpTokens, tokensunspendablepriv); - GetTokensCCaddress(cp, tokensunspendable, unspendablepk); - othertokenscond = MakeTokensCCcond1(cp->evalcode, unspendablepk); + // to spend from dual/three-eval EVAL_TOKEN+evalcode 'unspendable' pk: + GetTokensCCaddress(cp, unspendabletokensaddr, unspendablepk); // it may be a three-eval cc, if cp->additionalEvalcode2 is set + othertokenscond = MakeTokensCCcond1(cp->evalcode, cp->additionalTokensEvalcode2, unspendablepk); //Reorder vins so that for multiple normal vins all other except vin0 goes to the end //This is a must to avoid hardfork change of validation in every CC, because there could be maximum one normal vin at the begining with current validation. @@ -154,10 +156,11 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran else { Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey); - //fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s)\n",i,(double)utxovalues[i]/COIN,destaddr); - //std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " myaddr=" << myaddr << std::endl; - if( strcmp(destaddr,myaddr) == 0 ) + //fprintf(stderr,"FinalizeCCTx() vin.%d is CC %.8f -> (%s) vs %s\n",i,(double)utxovalues[i]/COIN,destaddr,cp->unspendableaddr2); + //std::cerr << "FinalizeCCtx() searching destaddr=" << destaddr << " for vin[" << i << "] satoshis=" << utxovalues[i] << std::endl; + if( strcmp(destaddr, myaddr) == 0 ) { + //fprintf(stderr, "FinalizeCCTx() matched cc myaddr (%s)\n", myaddr); privkey = myprivkey; cond = mycond; } @@ -165,33 +168,33 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { privkey = myprivkey; cond = mytokenscond; - //fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout CC addr.(%s)\n",mytokensaddr); + //fprintf(stderr,"FinalizeCCTx() matched dual-eval TokensCC1vout my token addr.(%s)\n",mytokensaddr); } else if (strcmp(destaddr, mysingletokensaddr) == 0) // if this is TokensCC1vout { privkey = myprivkey; cond = mysingletokenscond; - //fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout CC addr.(%s)\n", mytokensaddr); + //fprintf(stderr, "FinalizeCCTx() matched single-eval token CC1vout my token addr.(%s)\n", mytokensaddr); } else if ( strcmp(destaddr,unspendable) == 0 ) { privkey = unspendablepriv; cond = othercond; - //fprintf(stderr,"FinalizeCCTx() matched unspendable CC addr.(%s)\n",unspendable); + //fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable); } - else if (strcmp(destaddr, tokensunspendable) == 0) + else if (strcmp(destaddr, unspendabletokensaddr) == 0) { privkey = unspendablepriv; cond = othertokenscond; - //fprintf(stderr,"FinalizeCCTx() matched tokensunspendable CC addr.(%s)\n",unspendable); + //fprintf(stderr,"FinalizeCCTx() matched unspendabletokensaddr dual/three-eval CC addr.(%s)\n",unspendabletokensaddr); } // check if this is the 2nd additional evalcode + 'unspendable' cc addr: - else if ( strcmp(destaddr,cp->unspendableaddr2) == 0) + else if ( strcmp(destaddr, cp->unspendableaddr2) == 0) { //fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2); privkey = cp->unspendablepriv2; - if ( othercond2 == 0 ) - othercond2 = MakeCCcond1(cp->evalcode2, cp->unspendablepk2); + if( othercond2 == 0 ) + othercond2 = MakeCCcond1(cp->unspendableEvalcode2, cp->unspendablepk2); cond = othercond2; } // check if this is 3rd additional evalcode + 'unspendable' cc addr: @@ -199,26 +202,36 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran { //fprintf(stderr,"FinalizeCCTx() matched %s unspendable3!\n",cp->unspendableaddr3); privkey = cp->unspendablepriv3; - if ( othercond3 == 0 ) - othercond3 = MakeCCcond1(cp->evalcode3,cp->unspendablepk3); + if( othercond3 == 0 ) + othercond3 = MakeCCcond1(cp->unspendableEvalcode3, cp->unspendablepk3); cond = othercond3; } // check if this is spending from 1of2 cc coins addr: else if (strcmp(cp->coins1of2addr, destaddr) == 0) { //fprintf(stderr,"FinalizeCCTx() matched %s unspendable1of2!\n",cp->coins1of2addr); - privkey = myprivkey; + privkey = cp->coins1of2priv;//myprivkey; if (othercond1of2 == 0) othercond1of2 = MakeCCcond1of2(cp->evalcode, cp->coins1of2pk[0], cp->coins1of2pk[1]); cond = othercond1of2; } + else if ( strcmp(CC1of2CCaddr,destaddr) == 0 ) + { + //fprintf(stderr,"FinalizeCCTx() matched %s CC1of2CCaddr!\n",CC1of2CCaddr); + privkey = unspendablepriv; + if (condCC2 == 0) + condCC2 = MakeCCcond1of2(cp->evalcode,unspendablepk,unspendablepk); + cond = condCC2; + } // check if this is spending from 1of2 cc tokens addr: else if (strcmp(cp->tokens1of2addr, destaddr) == 0) { //fprintf(stderr,"FinalizeCCTx() matched %s cp->tokens1of2addr!\n", cp->tokens1of2addr); privkey = myprivkey; if (othercond1of2tokens == 0) - othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->tokens1of2pk[0], cp->tokens1of2pk[1]); + // NOTE: if additionalEvalcode2 is not set then it is dual-eval cc else three-eval cc + // TODO: verify evalcodes order if additionalEvalcode2 is not 0 + othercond1of2tokens = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, cp->tokens1of2pk[0], cp->tokens1of2pk[1]); cond = othercond1of2tokens; } else @@ -242,7 +255,7 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran if ( flag == 0 ) { fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr); - continue; + return(""); } } uint256 sighash = SignatureHash(CCPubKey(cond), mtx, i, SIGHASH_ALL, utxovalues[i],consensusBranchId, &txdata); @@ -264,9 +277,11 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran } } } else fprintf(stderr,"FinalizeCCTx couldnt find %s\n",mtx.vin[i].prevout.hash.ToString().c_str()); - } + } if ( mycond != 0 ) cc_free(mycond); + if ( condCC2 != 0 ) + cc_free(condCC2); if ( othercond != 0 ) cc_free(othercond); if ( othercond2 != 0 ) @@ -275,13 +290,23 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran cc_free(othercond3); if ( othercond4 != 0 ) cc_free(othercond4); + if ( othercond1of2 != 0 ) + cc_free(othercond1of2); + if ( othercond1of2tokens != 0 ) + cc_free(othercond1of2tokens); + if ( mytokenscond != 0 ) + cc_free(mytokenscond); + if ( mysingletokenscond != 0 ) + cc_free(mysingletokenscond); + if ( othertokenscond != 0 ) + cc_free(othertokenscond); std::string strHex = EncodeHexTx(mtx); if ( strHex.size() > 0 ) return(strHex); else return("0"); } -void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr) +void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr,bool ccflag) { int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector > addresses; n = (int32_t)strlen(coinaddr); @@ -290,7 +315,7 @@ void SetCCunspents(std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) @@ -300,7 +325,7 @@ void SetCCunspents(std::vector > &addressIndex,char *coinaddr) +void SetCCtxids(std::vector > &addressIndex,char *coinaddr,bool ccflag) { int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector > addresses; n = (int32_t)strlen(coinaddr); @@ -309,7 +334,7 @@ void SetCCtxids(std::vector > &addressIndex for (i=0; i<=n; i++) ptr[i] = coinaddr[i]; CBitcoinAddress address(addrstr); - if ( address.GetIndexKey(hashBytes, type) == 0 ) + if ( address.GetIndexKey(hashBytes, type, ccflag) == 0 ) return; addresses.push_back(std::make_pair(hashBytes,type)); for (std::vector >::iterator it = addresses.begin(); it != addresses.end(); it++) @@ -319,10 +344,10 @@ void SetCCtxids(std::vector > &addressIndex } } -int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout) +int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout,int32_t CCflag) { uint256 txid; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -332,17 +357,29 @@ int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout) return(0); } -int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag) +int32_t CCgettxout(uint256 txid,int32_t vout,int32_t mempoolflag,int32_t lockflag) { CCoins coins; + //fprintf(stderr,"CCgettxoud %s/v%d\n",txid.GetHex().c_str(),vout); if ( mempoolflag != 0 ) { - LOCK(mempool.cs); - CCoinsViewMemPool view(pcoinsTip, mempool); - if (!view.GetCoins(txid, coins)) - return(-1); - if ( myIsutxo_spentinmempool(txid,vout) != 0 ) - return(-1); + if ( lockflag != 0 ) + { + LOCK(mempool.cs); + CCoinsViewMemPool view(pcoinsTip, mempool); + if (!view.GetCoins(txid, coins)) + return(-1); + else if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) != 0 ) + return(-1); + } + else + { + CCoinsViewMemPool view(pcoinsTip, mempool); + if (!view.GetCoins(txid, coins)) + return(-1); + else if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) != 0 ) + return(-1); + } } else { @@ -366,10 +403,10 @@ int32_t CCgetspenttxid(uint256 &spenttxid,int32_t &vini,int32_t &height,uint256 return(0); } -int64_t CCaddress_balance(char *coinaddr) +int64_t CCaddress_balance(char *coinaddr,int32_t CCflag) { int64_t sum = 0; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { sum += it->second.satoshis; @@ -380,32 +417,33 @@ int64_t CCaddress_balance(char *coinaddr) int64_t CCfullsupply(uint256 tokenid) { uint256 hashBlock; int32_t numvouts; CTransaction tx; std::vector origpubkey; std::string name,description; - if ( GetTransaction(tokenid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) + if ( myGetTransaction(tokenid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) { if (DecodeTokenCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description)) { - return(tx.vout[0].nValue); + return(tx.vout[1].nValue); } } return(0); } -int64_t CCtoken_balance(char *coinaddr,uint256 tokenid) +int64_t CCtoken_balance(char *coinaddr,uint256 reftokenid) { - int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 assetid,assetid2,txid,hashBlock; + int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 tokenid,txid,hashBlock; std::vector vopretExtra; std::vector > unspentOutputs; uint8_t evalCode; - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0 ) { - char str[65]; fprintf(stderr,"check %s %.8f\n",uint256_str(str,txid),(double)it->second.satoshis/COIN); + char str[65]; std::vector voutTokenPubkeys; - if ( DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, assetid, voutTokenPubkeys, vopretExtra) != 0 && assetid == tokenid ) + std::vector> oprets; + if ( reftokenid==txid || (DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets) != 0 && reftokenid == tokenid)) { sum += it->second.satoshis; } @@ -471,16 +509,18 @@ int32_t CC_vinselect(int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t * int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,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; uint256 txid,hashBlock; std::vector vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; + int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; uint256 txid,hashBlock; std::vector vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up; #ifdef ENABLE_WALLET assert(pwalletMain != NULL); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); - utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); - threshold = total/(maxinputs+1); - if ( maxinputs > maxutxos ) - maxutxos = maxinputs; + utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; sum = 0; BOOST_FOREACH(const COutput& out, vecOutputs) { @@ -507,7 +547,7 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 if ( i != n ) continue; } - if ( myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { up = &utxos[n++]; up->txid = txid; @@ -515,7 +555,7 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 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 ) + if ( n >= maxinputs || sum >= total ) break; } } @@ -561,18 +601,19 @@ int64_t AddNormalinputs(CMutableTransaction &mtx,CPubKey mypk,int64_t total,int3 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; + int32_t abovei,belowi,ind,vout,i,n = 0; int64_t sum,threshold,above,below; int64_t remains,nValue,totalinputs = 0; char coinaddr[64]; uint256 txid,hashBlock; CTransaction tx; struct CC_utxo *utxos,*up; std::vector > unspentOutputs; - utxos = (struct CC_utxo *)calloc(maxutxos,sizeof(*utxos)); - threshold = total/(maxinputs+1); - if ( maxinputs > maxutxos ) - maxutxos = maxinputs; + utxos = (struct CC_utxo *)calloc(CC_MAXVINS,sizeof(*utxos)); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; sum = 0; Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,false); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -598,7 +639,7 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinput if ( i != n ) continue; } - if ( myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { up = &utxos[n++]; up->txid = txid; @@ -606,7 +647,7 @@ int64_t AddNormalinputs2(CMutableTransaction &mtx,int64_t total,int32_t maxinput 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 ) + if ( n >= maxinputs || sum >= total ) break; } } diff --git a/src/cc/CCutilbits.cpp b/src/cc/CCutilbits.cpp new file mode 100644 index 000000000..d50fb971c --- /dev/null +++ b/src/cc/CCutilbits.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +/* + CCutilbits.cpp has very low level functions that are universally useful for all contracts and have low dependency from other sources + */ + +#include "CCinclude.h" +#include "komodo_structs.h" + +int32_t unstringbits(char *buf,uint64_t bits) +{ + int32_t i; + for (i=0; i<8; i++,bits>>=8) + if ( (buf[i]= (char)(bits & 0xff)) == 0 ) + break; + buf[i] = 0; + return(i); +} + +uint64_t stringbits(char *str) +{ + uint64_t bits = 0; + if ( str == 0 ) + return(0); + int32_t i,n = (int32_t)strlen(str); + if ( n > 8 ) + n = 8; + for (i=n-1; i>=0; i--) + bits = (bits << 8) | (str[i] & 0xff); + //printf("(%s) -> %llx %llu\n",str,(long long)bits,(long long)bits); + return(bits); +} + +uint256 revuint256(uint256 txid) +{ + uint256 revtxid; int32_t i; + for (i=31; i>=0; i--) + ((uint8_t *)&revtxid)[31-i] = ((uint8_t *)&txid)[i]; + return(revtxid); +} + +char *uint256_str(char *dest,uint256 txid) +{ + int32_t i,j=0; + for (i=31; i>=0; i--) + sprintf(&dest[j++ * 2],"%02x",((uint8_t *)&txid)[i]); + dest[64] = 0; + return(dest); +} + +char *pubkey33_str(char *dest,uint8_t *pubkey33) +{ + int32_t i; + if ( pubkey33 != 0 ) + { + for (i=0; i<33; i++) + sprintf(&dest[i * 2],"%02x",pubkey33[i]); + } else dest[0] = 0; + return(dest); +} + +uint256 Parseuint256(const char *hexstr) +{ + uint256 txid; int32_t i; std::vector txidbytes(ParseHex(hexstr)); + memset(&txid,0,sizeof(txid)); + if ( strlen(hexstr) == 64 ) + { + for (i=31; i>=0; i--) + ((uint8_t *)&txid)[31-i] = ((uint8_t *)txidbytes.data())[i]; + } + return(txid); +} + +CPubKey buf2pk(uint8_t *buf33) +{ + CPubKey pk; int32_t i; uint8_t *dest; + dest = (uint8_t *)pk.begin(); + for (i=0; i<33; i++) + dest[i] = buf33[i]; + return(pk); +} + +CPubKey pubkey2pk(std::vector pubkey) +{ + CPubKey pk; int32_t i,n; uint8_t *dest,*pubkey33; + n = pubkey.size(); + dest = (uint8_t *)pk.begin(); + pubkey33 = (uint8_t *)pubkey.data(); + for (i=0; i pks; @@ -85,44 +86,68 @@ CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) std::vector thresholds; thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) ); - if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()! - thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc - thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc + if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()! + thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc + if( evalcode2 != 0 ) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode + thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc return CCNewThreshold(thresholds.size(), thresholds); } +// overload to make two-eval (token+evalcode) 1of2 cryptocondition: +CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) { + return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2); +} -CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) +// make three-eval (token+evalcode+evalcode2) cryptocondition: +CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk) { std::vector pks; pks.push_back(CCNewSecp256k1(pk)); std::vector thresholds; thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode))); - if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! - thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc - thresholds.push_back(CCNewThreshold(1, pks)); // signature + if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()! + thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc + if (evalcode2 != 0) + thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode + thresholds.push_back(CCNewThreshold(1, pks)); // signature return CCNewThreshold(thresholds.size(), thresholds); } +// overload to make two-eval (token+evalcode) cryptocondition: +CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) { + return MakeTokensCCcond1(evalcode, 0, pk); +} -CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) +// make three-eval (token+evalcode+evalcode2) 1of2 cc vout: +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2) { CTxOut vout; - CC *payoutCond = MakeTokensCCcond1of2(evalcode, pk1, pk2); + CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2); vout = CTxOut(nValue, CCPubKey(payoutCond)); cc_free(payoutCond); return(vout); } +// overload to make two-eval (token+evalcode) 1of2 cc vout: +CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) { + return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2); +} -CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) +// make three-eval (token+evalcode+evalcode2) cc vout: +CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk) { CTxOut vout; - CC *payoutCond = MakeTokensCCcond1(evalcode, pk); + CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk); vout = CTxOut(nValue, CCPubKey(payoutCond)); cc_free(payoutCond); return(vout); } +// overload to make two-eval (token+evalcode) cc vout: +CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) { + return MakeTokensCC1vout(evalcode, 0, nValue, pk); +} + CC* GetCryptoCondition(CScript const& scriptSig) { @@ -143,120 +168,53 @@ bool IsCCInput(CScript const& scriptSig) return true; } -int32_t unstringbits(char *buf,uint64_t bits) +bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime) { - int32_t i; - for (i=0; i<8; i++,bits>>=8) - if ( (buf[i]= (char)(bits & 0xff)) == 0 ) - break; - buf[i] = 0; - return(i); -} - -uint64_t stringbits(char *str) -{ - uint64_t bits = 0; - if ( str == 0 ) - return(0); - int32_t i,n = (int32_t)strlen(str); - if ( n > 8 ) - n = 8; - for (i=n-1; i>=0; i--) - bits = (bits << 8) | (str[i] & 0xff); - //printf("(%s) -> %llx %llu\n",str,(long long)bits,(long long)bits); - return(bits); -} - -uint256 revuint256(uint256 txid) -{ - uint256 revtxid; int32_t i; - for (i=31; i>=0; i--) - ((uint8_t *)&revtxid)[31-i] = ((uint8_t *)&txid)[i]; - return(revtxid); -} - -char *uint256_str(char *dest,uint256 txid) -{ - int32_t i,j=0; - for (i=31; i>=0; i--) - sprintf(&dest[j++ * 2],"%02x",((uint8_t *)&txid)[i]); - dest[64] = 0; - return(dest); -} - -char *pubkey33_str(char *dest,uint8_t *pubkey33) -{ - int32_t i; - if ( pubkey33 != 0 ) + int64_t interest; uint64_t valuein; + CCoinsViewCache &view = *pcoinsTip; + valuein = view.GetValueIn(height,&interest,tx,blocktime); + if ( valuein-tx.GetValueOut() > txfee ) { - for (i=0; i<33; i++) - sprintf(&dest[i * 2],"%02x",pubkey33[i]); - } else dest[0] = 0; - return(dest); -} - -uint256 Parseuint256(char *hexstr) -{ - uint256 txid; int32_t i; std::vector txidbytes(ParseHex(hexstr)); - memset(&txid,0,sizeof(txid)); - if ( strlen(hexstr) == 64 ) - { - for (i=31; i>=0; i--) - ((uint8_t *)&txid)[31-i] = ((uint8_t *)txidbytes.data())[i]; + //fprintf(stderr, "txfee.%li vs txfee.%li\n", valuein-tx.GetValueOut(), txfee); + return false; } - return(txid); -} - -CPubKey buf2pk(uint8_t *buf33) -{ - CPubKey pk; int32_t i; uint8_t *dest; - dest = (uint8_t *)pk.begin(); - for (i=0; i<33; i++) - dest[i] = buf33[i]; - return(pk); -} - -CPubKey pubkey2pk(std::vector pubkey) -{ - CPubKey pk; int32_t i,n; uint8_t *dest,*pubkey33; - n = pubkey.size(); - dest = (uint8_t *)pk.begin(); - pubkey33 = (uint8_t *)pubkey.data(); - for (i=0; ievalcode2 = evalcode; + cp->unspendableEvalcode2 = evalcode; cp->unspendablepk2 = pk; memcpy(cp->unspendablepriv2,priv,32); strcpy(cp->unspendableaddr2,coinaddr); } +// set yet another additional 'unspendable' addr void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr) { - cp->evalcode3 = evalcode; + cp->unspendableEvalcode3 = evalcode; cp->unspendablepk3 = pk; memcpy(cp->unspendablepriv3,priv,32); strcpy(cp->unspendableaddr3,coinaddr); } // set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 cryptocondition vout: -void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr) +void CCaddr1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2,uint8_t *priv,char *coinaddr) { cp->coins1of2pk[0] = pk1; cp->coins1of2pk[1] = pk2; - strcpy(cp->coins1of2addr, coinaddr); + memcpy(cp->coins1of2priv,priv,32); + strcpy(cp->coins1of2addr,coinaddr); } -// set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 tokens cryptocondition vout: -void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *coinaddr) +// set pubkeys, myprivkey and 1of2 cc addr for spending from 1of2 token cryptocondition vout +// to get tokenaddr use GetTokensCCaddress() +void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, char *tokenaddr) { cp->tokens1of2pk[0] = pk1; cp->tokens1of2pk[1] = pk2; - strcpy(cp->tokens1of2addr, coinaddr); + strcpy(cp->tokens1of2addr, tokenaddr); } bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) @@ -271,6 +229,18 @@ bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) return(false); } +bool GetCustomscriptaddress(char *destaddr,const CScript &scriptPubKey,uint8_t taddr,uint8_t prefix, uint8_t prefix2) +{ + CTxDestination address; txnouttype whichType; + if ( ExtractDestination(scriptPubKey,address) != 0 ) + { + strcpy(destaddr,(char *)CCustomBitcoinAddress(address,taddr,prefix,prefix2).ToString().c_str()); + return(true); + } + //fprintf(stderr,"ExtractDestination failed\n"); + return(false); +} + bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn, CTransaction &txOut, std::vector> &preConditions, std::vector> ¶ms) { @@ -331,6 +301,16 @@ CPubKey CCtxidaddr(char *txidaddr,uint256 txid) return(pk); } +CPubKey CCCustomtxidaddr(char *txidaddr,uint256 txid,uint8_t taddr,uint8_t prefix,uint8_t prefix2) +{ + uint8_t buf33[33]; CPubKey pk; + buf33[0] = 0x02; + endiancpy(&buf33[1],(uint8_t *)&txid,32); + pk = buf2pk(buf33); + GetCustomscriptaddress(txidaddr,CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG,taddr,prefix,prefix2); + return(pk); +} + bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk) { CC *payoutCond; @@ -351,11 +331,11 @@ bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk) return(_GetCCaddress(destaddr,cp->evalcode,pk)); } -bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, CPubKey pk) +bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, uint8_t evalcode2, CPubKey pk) { CC *payoutCond; destaddr[0] = 0; - if ((payoutCond = MakeTokensCCcond1(evalcode, pk)) != 0) + if ((payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk)) != 0) { Getscriptaddress(destaddr, CCPubKey(payoutCond)); cc_free(payoutCond); @@ -363,12 +343,13 @@ bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, CPubKey pk) return(destaddr[0] != 0); } +// get scriptPubKey adddress for three/dual eval token cc vout bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk) { destaddr[0] = 0; if (pk.size() == 0) pk = GetUnspendable(cp, 0); - return(_GetTokensCCaddress(destaddr, cp->evalcode, pk)); + return(_GetTokensCCaddress(destaddr, cp->evalcode, cp->additionalTokensEvalcode2, pk)); } @@ -384,11 +365,12 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK return(destaddr[0] != 0); } +// get scriptPubKey adddress for three/dual eval token 1of2 cc vout bool GetTokensCCaddress1of2(struct CCcontract_info *cp, char *destaddr, CPubKey pk, CPubKey pk2) { CC *payoutCond; destaddr[0] = 0; - if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, pk, pk2)) != 0) + if ((payoutCond = MakeTokensCCcond1of2(cp->evalcode, cp->additionalTokensEvalcode2, pk, pk2)) != 0) // if additionalTokensEvalcode2 not set then it is dual-eval cc else three-eval cc { Getscriptaddress(destaddr, CCPubKey(payoutCond)); cc_free(payoutCond); @@ -442,6 +424,20 @@ bool PreventCC(Eval* eval,const CTransaction &tx,int32_t preventCCvins,int32_t n return(true); } +bool priv2addr(char *coinaddr,uint8_t *buf33,uint8_t priv32[32]) +{ + CKey priv; CPubKey pk; int32_t i; uint8_t *src; + priv.SetKey32(priv32); + pk = priv.GetPubKey(); + if ( buf33 != 0 ) + { + src = (uint8_t *)pk.begin(); + for (i=0; i<33; i++) + buf33[i] = src[i]; + } + return(Getscriptaddress(coinaddr, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG)); +} + std::vector Mypubkey() { extern uint8_t NOTARY_PUBKEY33[33]; @@ -456,7 +452,7 @@ std::vector Mypubkey() bool Myprivkey(uint8_t myprivkey[]) { - char coinaddr[64]; std::string strAddress; char *dest; int32_t i,n; CBitcoinAddress address; CKeyID keyID; CKey vchSecret; + char coinaddr[64],checkaddr[64]; std::string strAddress; char *dest; int32_t i,n; CBitcoinAddress address; CKeyID keyID; CKey vchSecret; uint8_t buf33[33]; if ( Getscriptaddress(coinaddr,CScript() << Mypubkey() << OP_CHECKSIG) != 0 ) { n = (int32_t)strlen(coinaddr); @@ -477,7 +473,13 @@ bool Myprivkey(uint8_t myprivkey[]) fprintf(stderr,"0x%02x, ",myprivkey[i]); fprintf(stderr," found privkey for %s!\n",dest); } - return(true); + if ( priv2addr(checkaddr,buf33,myprivkey) != 0 ) + { + if ( buf2pk(buf33) == Mypubkey() && strcmp(checkaddr,coinaddr) == 0 ) + return(true); + else printf("mismatched privkey -> addr %s vs %s\n",checkaddr,coinaddr); + } + return(false); } #endif } @@ -495,7 +497,7 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv) void CCclearvars(struct CCcontract_info *cp) { - cp->evalcode2 = cp->evalcode3 = 0; + cp->unspendableEvalcode2 = cp->unspendableEvalcode3 = 0; cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0; } @@ -530,6 +532,95 @@ int64_t CCduration(int32_t &numblocks,uint256 txid) return(duration); } +uint256 CCOraclesReverseScan(char const *logcategory,uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid) +{ + CTransaction tx; uint256 hash,mhash,bhash,hashBlock,oracletxid; int32_t len,len2,numvouts; + int64_t val,merkleht; CPubKey pk; std::vectordata; char str[65],str2[65]; + + txid = zeroid; + LogPrint(logcategory,"start reverse scan %s\n",uint256_str(str,batontxid)); + while ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) + { + LogPrint(logcategory,"check %s\n",uint256_str(str,batontxid)); + if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,bhash,pk,data) == 'D' && oracletxid == reforacletxid ) + { + LogPrint(logcategory,"decoded %s\n",uint256_str(str,batontxid)); + if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height ) + { + len = oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()); + len2 = oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()); + + LogPrint(logcategory,"found merkleht.%d len.%d len2.%d %s %s\n",(int32_t)merkleht,len,len2,uint256_str(str,hash),uint256_str(str2,mhash)); + if ( len == sizeof(hash)+sizeof(int32_t) && len2 == 2*sizeof(mhash)+sizeof(int32_t) && mhash != zeroid ) + { + txid = batontxid; + LogPrint(logcategory,"set txid\n"); + return(mhash); + } + else + { + LogPrint(logcategory,"missing hash\n"); + return(zeroid); + } + } + else LogPrint(logcategory,"height.%d vs search ht.%d\n",(int32_t)merkleht,(int32_t)height); + batontxid = bhash; + LogPrint(logcategory,"new hash %s\n",uint256_str(str,batontxid)); + } else break; + } + LogPrint(logcategory,"end of loop\n"); + return(zeroid); +} + +int32_t myIs_coinaddr_inmempoolvout(char const *logcategory,char *coinaddr) +{ + int32_t i,n; char destaddr[64]; + BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) + { + const CTransaction &tx = e.GetTx(); + if ( (n= tx.vout.size()) > 0 ) + { + const uint256 &txid = tx.GetHash(); + for (i=0; i > addressIndex; + CCtxidaddr(txidaddr,cointxid); + SetCCtxids(addressIndex,txidaddr,true); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + return(-1); + } + return(myIs_coinaddr_inmempoolvout(logcategory,txidaddr)); +} + +/* Get the block merkle root for a proof + * IN: proofData + * OUT: merkle root + * OUT: transaction IDS + */ +uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids) +{ + CMerkleBlock merkleBlock; + if (!E_UNMARSHAL(proofData, ss >> merkleBlock)) + return uint256(); + return merkleBlock.txn.ExtractMatches(txids); +} + bool komodo_txnotarizedconfirmed(uint256 txid) { char str[65]; @@ -614,6 +705,9 @@ bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector param from_mempool = 1; height &= ((1<<30) - 1); } + if (cp->validate == NULL) + return eval->Invalid("validation not supported for eval code"); + //fprintf(stderr,"KOMODO_CONNECTING.%d mempool.%d vs CCactive.%d\n",height,from_mempool,KOMODO_CCACTIVATE); // there is a chance CC tx is valid in mempool, but invalid when in block, so we cant filter duplicate requests. if any of the vins are spent, for example //txid = ctx.GetHash(); diff --git a/src/cc/Makefile b/src/cc/Makefile index 52db18fca..3e988f279 100644 --- a/src/cc/Makefile +++ b/src/cc/Makefile @@ -13,7 +13,7 @@ $(info $(OS)) TARGET = ../libcc.so TARGET_DARWIN = ../libcc.dylib TARGET_WIN = ../libcc.dll -SOURCES = cclib.cpp +SOURCES = cclib.cpp #HEADERS = $(shell echo ../cryptoconditions/include/*.h) all: $(TARGET) diff --git a/src/cc/Makefile_rogue b/src/cc/Makefile_rogue new file mode 100644 index 000000000..baf8767aa --- /dev/null +++ b/src/cc/Makefile_rogue @@ -0,0 +1,38 @@ +SHELL = /bin/sh +CC = gcc +CC_DARWIN = g++-6 +CC_WIN = x86_64-w64-mingw32-gcc-posix +CFLAGS_DARWIN = -DBUILD_ROGUE -std=c++11 -arch x86_64 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -Wl,-undefined -Wl,dynamic_lookup -Wno-write-strings -shared -dynamiclib +CFLAGS = -Wno-write-strings -DBUILD_ROGUE -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared +CFLAGS_WIN = -Wno-write-strings -DBUILD_ROGUE -std=c++11 -I./rogue/x86_64-w64-mingw32/include -I./rogue/x86_64-w64-mingw32/include/ncursesw -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared +DEBUGFLAGS = -O0 -D _DEBUG +RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program +$(info $(OS)) +OS := $(shell uname -s) +$(info $(OS)) +TARGET = librogue.so +TARGET_DARWIN = librogue.dylib +TARGET_WIN = librogue.dll +SOURCES = cclib.cpp +#HEADERS = $(shell echo ../cryptoconditions/include/*.h) -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ + +all: $(TARGET) + +$(TARGET): $(SOURCES) + $(info Building cclib to src/) +ifeq ($(OS),Darwin) + $(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) -c $(SOURCES) + cp $(TARGET_DARWIN) ../libcc.dylib +else ifeq ($(HOST),x86_64-w64-mingw32) + $(info WINDOWS) + $(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) -c $(SOURCES) + cp $(TARGET_WIN) ../libcc.dll +#else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host +else + $(info LINUX) + $(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) -c $(SOURCES) + cp $(TARGET) ../libcc.so +endif + +clean: + rm -rf $(TARGET) diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index 8d133701c..9ae4cc1eb 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -14,6 +14,7 @@ ******************************************************************************/ #include "CCassets.h" +#include "CCtokens.h" /* Assets can be created or transferred. @@ -129,21 +130,21 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn) { static uint256 zero; - CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2; - int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; - int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector origpubkey,tmporigpubkey,ignorepubkey; + CTxDestination address; + CTransaction vinTx, createTx; + uint256 hashBlock, assetid, assetid2; + int32_t i,starti, numvins, numvouts, preventCCvins, preventCCvouts; + int64_t remaining_price, nValue, assetoshis, outputsDummy,inputs,tmpprice,totalunits,ignore; + std::vector origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy; uint8_t funcid, evalCodeInOpret; - char destaddr[64], origaddr[64], assetsCCaddr[64], userTokensCCaddr[64]; //, signleEvalTokensCCaddr[64]; + char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64]; + char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64]; //return true; - //CPubKey unspendableTokensPk = GetUnspendable(cpTokens, NULL); - //CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, NULL); - //GetCCaddress(cpTokens, tokensUnspendableCCaddr, unspendableTokensPk); - numvins = tx.vin.size(); numvouts = tx.vout.size(); - outputs = inputs = 0; + outputsDummy = inputs = 0; preventCCvins = preventCCvouts = -1; // add specific chains exceptions for old token support: @@ -153,16 +154,28 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190) return true; + // add specific chains exceptions for old token support: + if (strcmp(ASSETCHAINS_SYMBOL, "SEC") == 0 && chainActive.Height() <= 144073) + return true; + + if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190) + return true; + if (numvouts == 0) return eval->Invalid("AssetValidate: no vouts"); if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 ) return eval->Invalid("AssetValidate: invalid opreturn payload"); + // non-fungible tokens support: + GetNonfungibleData(assetid, vopretNonfungible); + if (vopretNonfungible.size() > 0) + cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0]; + // find dual-eval tokens unspendable addr: - char tokensUnspendableAddr[64],origpubkeyCCaddr[64]; - GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - GetCCaddress(cpAssets, origpubkeyCCaddr, origpubkey); + GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL)); + // this is for marker validation: + GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey); // we need this for validating single-eval tokens' vins/vous: struct CCcontract_info *cpTokens, tokensC; @@ -171,13 +184,13 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti // find single-eval token user cc addr: //GetCCaddress(cpTokens, signleEvalTokensCCaddr, pubkey2pk(origpubkey)); - fprintf(stderr,"AssetValidate (%c)\n",funcid); + //fprintf(stderr,"AssetValidate (%c)\n",funcid); if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 ) return eval->Invalid("cant find asset create txid"); else if( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2, createTx, hashBlock) == 0 ) return eval->Invalid("cant find asset2 create txid"); - else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) + else if( IsCCInput(tx.vin[0].scriptSig) != 0 ) // vin0 should be normal vin return eval->Invalid("illegal asset vin0"); else if( numvouts < 2 ) return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below @@ -191,7 +204,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti if( assetid == zero ) return eval->Invalid("illegal assetid"); - else if (!AssetCalcAmounts(cpAssets, inputs, outputs, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs + else if (!AssetCalcAmounts(cpAssets, inputs, outputsDummy/*outputsDummy is calculated incorrectly but not used*/, eval, tx, assetid)) { // Only set inputs and outputs. NOTE: we do not need to check cc inputs == cc outputs return false; // returns false if some problems with reading vintxes } } @@ -226,9 +239,13 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.1: CC output for marker //vout.2: normal output for change (if any) // vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey] + + // as we don't use tokenconvert we should not be here: + return eval->Invalid("invalid asset funcid (b)"); + if( remaining_price == 0 ) return eval->Invalid("illegal null amount for buyoffer"); - else if( ConstrainVout(tx.vout[0],1,cpAssets->unspendableCCaddr,0) == 0 ) + else if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr,0) == 0 ) // coins to assets unspendable cc addr return eval->Invalid("invalid vout for buyoffer"); preventCCvins = 1; preventCCvouts = 1; @@ -243,13 +260,13 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.1: vin.2 back to users pubkey //vout.2: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['o'] - if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) + if( (nValue= AssetValidateBuyvin(cpAssets, eval, tmpprice, tmporigpubkey, origCCaddrDummy, origNormalAddr, tx, assetid)) == 0 ) return(false); - else if( ConstrainVout(tx.vout[0],0, origaddr, nValue) == 0 ) + else if( ConstrainVout(tx.vout[0],0, origNormalAddr, nValue) == 0 ) return eval->Invalid("invalid refund for cancelbuy"); preventCCvins = 3; preventCCvouts = 0; - fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origaddr); + //fprintf(stderr,"cancelbuy validated to origaddr.(%s)\n",origNormalAddr); break; case 'B': // fillbuy: @@ -264,7 +281,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey] preventCCvouts = 4; - if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, assetsCCaddr, origaddr, tx, assetid)) == 0 ) + if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillbuy"); @@ -274,28 +291,29 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti { if( nValue != tx.vout[0].nValue + tx.vout[1].nValue ) return eval->Invalid("locked value doesnt match vout0+1 fillbuy"); - else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) + else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present { - if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, 0) == 0 ) // tokens on user cc addr + if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) return eval->Invalid("vout2 doesnt go to origpubkey fillbuy"); else if ( inputs != tx.vout[2].nValue + tx.vout[4].nValue ) return eval->Invalid("asset inputs doesnt match vout2+3 fillbuy"); + preventCCvouts ++; } - else if( ConstrainVout(tx.vout[2], 1, assetsCCaddr, inputs) == 0 ) // tokens on user cc addr + else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) // tokens to originator cc addr (tokens+nonfungible evals) return eval->Invalid("vout2 doesnt match inputs fillbuy"); - else if( ConstrainVout(tx.vout[1],0,0,0) == 0 ) + else if( ConstrainVout(tx.vout[1], 0, NULL, 0) == 0 ) return eval->Invalid("vout1 is CC for fillbuy"); - else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) + else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to asset cc addr return eval->Invalid("invalid marker for original pubkey"); else if( ValidateBidRemainder(remaining_price, tx.vout[0].nValue, nValue, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) return eval->Invalid("mismatched remainder for fillbuy"); else if( remaining_price != 0 ) { - if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins on asset unspendable cc addr + if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy"); } } - fprintf(stderr,"fillbuy validated\n"); + //fprintf(stderr,"fillbuy validated\n"); break; //case 'e': // selloffer // break; // disable swaps @@ -308,25 +326,30 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.3: normal output for change (if any) //'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey] //'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey] + + // as we don't use tokenconvert we should not be here: + return eval->Invalid("invalid asset funcid (s)"); + preventCCvouts = 2; if( remaining_price == 0 ) return eval->Invalid("illegal null remaining_price for selloffer"); if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) return eval->Invalid("invalid normal vout1 for sellvin"); - if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // cc change + if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change presents { preventCCvouts++; - if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // check also cc vout[0] + if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here! return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs ) return eval->Invalid("mismatched vout0+vout2 total for selloffer"); } - else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // no cc change, just vout[0] + // no cc change: + else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here! return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer"); //fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price); break; - case 'x': // cancel + case 'x': // cancel sell //vin.0: normal input //vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx //vin.2: CC marker from selloffer for txfee @@ -335,9 +358,9 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.2: normal output for change (if any) //vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid] - if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) // NOTE: + if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE: return(false); - else if( ConstrainVout(tx.vout[0], 1, userTokensCCaddr, assetoshis) == 0 ) + else if( ConstrainVout(tx.vout[0], 1, origTokensCCaddr, assetoshis) == 0 ) // tokens returning to originator cc addr return eval->Invalid("invalid vout for cancel"); preventCCvins = 3; preventCCvouts = 1; @@ -353,7 +376,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti //vout.3: normal output for change (if any) //'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey] - if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) + if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 4 ) return eval->Invalid("not enough vouts for fillask"); @@ -365,21 +388,19 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti return eval->Invalid("locked value doesnt match vout0+1 fillask"); if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false ) return eval->Invalid("mismatched remainder for fillask"); - else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 ) + else if( ConstrainVout(tx.vout[1], 1, NULL, 0) == 0 ) // do not check token buyer's cc addr return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[2], 0, origaddr, 0) == 0 ) + else if( ConstrainVout(tx.vout[2], 0, origNormalAddr, 0) == 0 ) // coins to originator normal addr return eval->Invalid("normal vout1 for fillask"); - else if( ConstrainVout(tx.vout[3], 1, origpubkeyCCaddr, 10000) == 0 ) + else if( ConstrainVout(tx.vout[3], 1, origAssetsCCaddr, 10000) == 0 ) // marker to originator asset cc addr return eval->Invalid("invalid marker for original pubkey"); else if( remaining_price != 0 ) { - //char tokensUnspendableAddr[64]; - //GetTokensCCaddress(cpAssets, tokensUnspendableAddr, GetUnspendable(cpAssets, NULL)); - if ( ConstrainVout(tx.vout[0], 1, tokensUnspendableAddr /*(char *)cpAssets->unspendableCCaddr*/, 0) == 0 ) + if ( ConstrainVout(tx.vout[0], 1, tokensDualEvalUnspendableCCaddr, 0) == 0 ) return eval->Invalid("mismatched vout0 assets dual unspendable CCaddr for fill sell"); } } - fprintf(stderr,"fill validated\n"); + //fprintf(stderr,"fill validated\n"); break; case 'E': // fillexchange ////////// not implemented yet //////////// @@ -399,7 +420,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti // eval->Invalid("asset2 inputs != outputs"); ////////// not implemented yet //////////// - if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, userTokensCCaddr, origaddr, tx, assetid)) == 0 ) + if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) return(false); else if( numvouts < 3 ) return eval->Invalid("not enough vouts for fillex"); @@ -412,7 +433,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) ////////// not implemented yet //////////// { - if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, 0) == 0 ) + if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, 0) == 0 ) return eval->Invalid("vout2 doesnt go to origpubkey fillex"); else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue ) { @@ -421,7 +442,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti } } ////////// not implemented yet //////////// - else if( ConstrainVout(tx.vout[2], 1, userTokensCCaddr, inputs) == 0 ) + else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 ) return eval->Invalid("vout2 doesnt match inputs fillex"); else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 ) return eval->Invalid("vout1 is CC for fillex"); @@ -438,7 +459,7 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti } } ////////// not implemented yet //////////// - fprintf(stderr,"fill validated\n"); + //fprintf(stderr,"fill validated\n"); break; default: @@ -448,8 +469,8 @@ bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransacti } // what does this do? - bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); - std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl; + bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // seems we do not need this call as we already checked vouts well + //std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl; return (bPrevent); } diff --git a/src/cc/auction.cpp b/src/cc/auction.cpp index 0858facfa..7521256c7 100644 --- a/src/cc/auction.cpp +++ b/src/cc/auction.cpp @@ -124,7 +124,7 @@ int64_t AddAuctionInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPu char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 971258d90..ee52dadcf 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -28,25 +28,298 @@ #include "crosschain.h" #define FAUCET2SIZE COIN +#define EVAL_FAUCET2 EVAL_FIRSTUSER -struct CClib_rpcinfo -{ - char *method,*help; - int32_t numrequiredargs,maxargs; // frontloaded with required - uint8_t funcid; -} -CClib_methods[] = -{ - { (char *)"faucet2_fund", (char *)"amount", 1, 1, 'F' }, - { (char *)"faucet2_get", (char *)"", 0, 0, 'G' }, -}; +#ifdef BUILD_ROGUE +#define EVAL_ROGUE 17 +std::string MYCCLIBNAME = (char *)"rogue"; -std::string MYCCLIBNAME = (char *)"faucet2"; + +#elif BUILD_CUSTOMCC +#include "customcc.h" + +#elif BUILD_GAMESCC +#include "gamescc.h" + +#else +#define EVAL_SUDOKU 17 +#define EVAL_MUSIG 18 +#define EVAL_DILITHIUM 19 +std::string MYCCLIBNAME = (char *)"sudoku"; +#endif + +#ifndef BUILD_GAMESCC +void komodo_netevent(std::vector payload) {} +#endif + +extern std::string MYCCLIBNAME; char *CClib_name() { return((char *)MYCCLIBNAME.c_str()); } +struct CClib_rpcinfo +{ + char *CCname,*method,*help; + int32_t numrequiredargs,maxargs; + uint8_t funcid,evalcode; +} + +CClib_methods[] = +{ + { (char *)"faucet2", (char *)"fund", (char *)"amount", 1, 1, 'F', EVAL_FAUCET2 }, + { (char *)"faucet2", (char *)"get", (char *)"", 0, 0, 'G', EVAL_FAUCET2 }, +#ifdef BUILD_ROGUE + { (char *)"rogue", (char *)"newgame", (char *)"maxplayers buyin", 0, 2, 'G', EVAL_ROGUE }, + { (char *)"rogue", (char *)"gameinfo", (char *)"gametxid", 1, 1, 'T', EVAL_ROGUE }, + { (char *)"rogue", (char *)"pending", (char *)"", 0, 0, 'P', EVAL_ROGUE }, + { (char *)"rogue", (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_ROGUE }, + { (char *)"rogue", (char *)"keystrokes", (char *)"gametxid keystrokes", 2, 2, 'K', EVAL_ROGUE }, + { (char *)"rogue", (char *)"bailout", (char *)"gametxid", 1, 1, 'Q', EVAL_ROGUE }, + { (char *)"rogue", (char *)"highlander", (char *)"gametxid", 1, 1, 'H', EVAL_ROGUE }, + { (char *)"rogue", (char *)"playerinfo", (char *)"playertxid", 1, 1, 'I', EVAL_ROGUE }, + { (char *)"rogue", (char *)"players", (char *)"", 0, 0, 'D', EVAL_ROGUE }, + { (char *)"rogue", (char *)"games", (char *)"", 0, 0, 'F', EVAL_ROGUE }, + { (char *)"rogue", (char *)"setname", (char *)"pname", 1, 1, 'N', EVAL_ROGUE }, + { (char *)"rogue", (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, 'X', EVAL_ROGUE }, +#elif BUILD_CUSTOMCC + RPC_FUNCS +#elif BUILD_GAMESCC + RPC_FUNCS +#else + { (char *)"sudoku", (char *)"gen", (char *)"", 0, 0, 'G', EVAL_SUDOKU }, + { (char *)"sudoku", (char *)"txidinfo", (char *)"txid", 1, 1, 'T', EVAL_SUDOKU }, + { (char *)"sudoku", (char *)"pending", (char *)"", 0, 0, 'U', EVAL_SUDOKU }, + { (char *)"sudoku", (char *)"solution", (char *)"txid solution timestamps[81]", 83, 83, 'S', EVAL_SUDOKU }, + { (char *)"musig", (char *)"calcmsg", (char *)"sendtxid scriptPubKey", 2, 2, 'C', EVAL_MUSIG }, + { (char *)"musig", (char *)"combine", (char *)"pubkeys ...", 2, 999999999, 'P', EVAL_MUSIG }, + { (char *)"musig", (char *)"session", (char *)"myindex,numsigners,combined_pk,pkhash,msg32", 5, 5, 'R', EVAL_MUSIG }, + { (char *)"musig", (char *)"commit", (char *)"pkhash,ind,commitment", 3, 3, 'H', EVAL_MUSIG }, + { (char *)"musig", (char *)"nonce", (char *)"pkhash,ind,nonce", 3, 3, 'N', EVAL_MUSIG }, + { (char *)"musig", (char *)"partialsig", (char *)"pkhash,ind,partialsig", 3, 3, 'S', EVAL_MUSIG }, + { (char *)"musig", (char *)"verify", (char *)"msg sig pubkey", 3, 3, 'V', EVAL_MUSIG }, + { (char *)"musig", (char *)"send", (char *)"combined_pk amount", 2, 2, 'x', EVAL_MUSIG }, + { (char *)"musig", (char *)"spend", (char *)"sendtxid sig scriptPubKey", 3, 3, 'y', EVAL_MUSIG }, + { (char *)"dilithium", (char *)"keypair", (char *)"[hexseed]", 0, 1, 'K', EVAL_DILITHIUM }, + { (char *)"dilithium", (char *)"register", (char *)"handle, [hexseed]", 1, 2, 'R', EVAL_DILITHIUM }, + { (char *)"dilithium", (char *)"handleinfo", (char *)"handle", 1, 1, 'I', EVAL_DILITHIUM }, + { (char *)"dilithium", (char *)"sign", (char *)"msg [hexseed]", 1, 2, 'S', EVAL_DILITHIUM }, + { (char *)"dilithium", (char *)"verify", (char *)"pubtxid msg sig", 3, 3, 'V', EVAL_DILITHIUM }, + { (char *)"dilithium", (char *)"send", (char *)"handle pubtxid amount", 3, 3, 'x', EVAL_DILITHIUM }, + { (char *)"dilithium", (char *)"spend", (char *)"sendtxid scriptPubKey [hexseed]", 2, 3, 'y', EVAL_DILITHIUM }, + { (char *)"dilithium", (char *)"Qsend", (char *)"mypubtxid hexseed/'mypriv' destpubtxid,amount, ...", 4, 66, 'Q', EVAL_DILITHIUM }, +#endif +}; + std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params); +#ifdef BUILD_ROGUE +int32_t rogue_replay(uint64_t seed,int32_t sleepmillis); +bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); + +UniValue rogue_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue rogue_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +#else +bool sudoku_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); +UniValue sudoku_txidinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue sudoku_generate(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue sudoku_solution(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue sudoku_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +bool musig_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); +UniValue musig_calcmsg(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_combine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_session(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_commit(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_nonce(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_partialsig(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +bool dilithium_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); +UniValue dilithium_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue dilithium_handleinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue dilithium_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue dilithium_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue dilithium_keypair(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue dilithium_sign(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue dilithium_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue dilithium_Qsend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +#endif + +cJSON *cclib_reparse(int32_t *nump,char *jsonstr) // assumes origparams will be freed by caller +{ + cJSON *params; char *newstr; int32_t i,j; + *nump = 0; + if ( jsonstr != 0 ) + { + if ( jsonstr[0] == '"' && jsonstr[strlen(jsonstr)-1] == '"' ) + { + jsonstr[strlen(jsonstr)-1] = 0; + jsonstr++; + } + newstr = (char *)malloc(strlen(jsonstr)+1); + for (i=j=0; jsonstr[i]!=0; i++) + { + if ( jsonstr[i] == '%' && jsonstr[i+1] == '2' && jsonstr[i+2] == '2' ) + { + newstr[j++] = '"'; + i += 2; + } + else if ( jsonstr[i] == '\'' ) + newstr[j++] = '"'; + else newstr[j++] = jsonstr[i]; + } + newstr[j] = 0; + params = cJSON_Parse(newstr); + if ( 0 && params != 0 ) + printf("new.(%s) -> %s\n",newstr,jprint(params,0)); + free(newstr); + *nump = cJSON_GetArraySize(params); + //free(origparams); + } else params = 0; + return(params); +} + +UniValue CClib_method(struct CCcontract_info *cp,char *method,char *jsonstr) +{ + UniValue result(UniValue::VOBJ); uint64_t txfee = 10000; int32_t m; cJSON *params = cclib_reparse(&m,jsonstr); + fprintf(stderr,"method.(%s) -> (%s)\n",jsonstr!=0?jsonstr:"",params!=0?jprint(params,0):""); +#ifdef BUILD_ROGUE + if ( cp->evalcode == EVAL_ROGUE ) + { + if ( strcmp(method,"newgame") == 0 ) + return(rogue_newgame(txfee,cp,params)); + else if ( strcmp(method,"pending") == 0 ) + return(rogue_pending(txfee,cp,params)); + else if ( strcmp(method,"gameinfo") == 0 ) + return(rogue_gameinfo(txfee,cp,params)); + else if ( strcmp(method,"register") == 0 ) + return(rogue_register(txfee,cp,params)); + else if ( strcmp(method,"keystrokes") == 0 ) + return(rogue_keystrokes(txfee,cp,params)); + else if ( strcmp(method,"bailout") == 0 ) + return(rogue_bailout(txfee,cp,params)); + else if ( strcmp(method,"highlander") == 0 ) + return(rogue_highlander(txfee,cp,params)); + else if ( strcmp(method,"extract") == 0 ) + return(rogue_extract(txfee,cp,params)); + else if ( strcmp(method,"playerinfo") == 0 ) + return(rogue_playerinfo(txfee,cp,params)); + else if ( strcmp(method,"players") == 0 ) + return(rogue_players(txfee,cp,params)); + else if ( strcmp(method,"games") == 0 ) + return(rogue_games(txfee,cp,params)); + else if ( strcmp(method,"setname") == 0 ) + return(rogue_setname(txfee,cp,params)); + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid rogue method")); + result.push_back(Pair("method",method)); + return(result); + } + } +#elif BUILD_CUSTOMCC + CUSTOM_DISPATCH +#elif BUILD_GAMESCC + CUSTOM_DISPATCH +#else + if ( cp->evalcode == EVAL_SUDOKU ) + { + //printf("CClib_method params.%p\n",params); + if ( strcmp(method,"txidinfo") == 0 ) + return(sudoku_txidinfo(txfee,cp,params)); + else if ( strcmp(method,"gen") == 0 ) + return(sudoku_generate(txfee,cp,params)); + else if ( strcmp(method,"solution") == 0 ) + return(sudoku_solution(txfee,cp,params)); + else if ( strcmp(method,"pending") == 0 ) + return(sudoku_pending(txfee,cp,params)); + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid sudoku method")); + result.push_back(Pair("method",method)); + return(result); + } + } + else if ( cp->evalcode == EVAL_MUSIG ) + { + //printf("CClib_method params.%p\n",params); + if ( strcmp(method,"combine") == 0 ) + return(musig_combine(txfee,cp,params)); + else if ( strcmp(method,"calcmsg") == 0 ) + return(musig_calcmsg(txfee,cp,params)); + else if ( strcmp(method,"session") == 0 ) + return(musig_session(txfee,cp,params)); + else if ( strcmp(method,"commit") == 0 ) + return(musig_commit(txfee,cp,params)); + else if ( strcmp(method,"nonce") == 0 ) // returns combined nonce if ready + return(musig_nonce(txfee,cp,params)); + else if ( strcmp(method,"partialsig") == 0 ) + return(musig_partialsig(txfee,cp,params)); + else if ( strcmp(method,"verify") == 0 ) + return(musig_verify(txfee,cp,params)); + else if ( strcmp(method,"send") == 0 ) + return(musig_send(txfee,cp,params)); + else if ( strcmp(method,"spend") == 0 ) + return(musig_spend(txfee,cp,params)); + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid musig method")); + result.push_back(Pair("method",method)); + return(result); + } + } + else if ( cp->evalcode == EVAL_DILITHIUM ) + { + if ( strcmp(method,"Qsend") == 0 ) + return(dilithium_Qsend(txfee,cp,params)); + else if ( strcmp(method,"send") == 0 ) + return(dilithium_send(txfee,cp,params)); + else if ( strcmp(method,"spend") == 0 ) + return(dilithium_spend(txfee,cp,params)); + else if ( strcmp(method,"keypair") == 0 ) + return(dilithium_keypair(txfee,cp,params)); + else if ( strcmp(method,"register") == 0 ) + return(dilithium_register(txfee,cp,params)); + else if ( strcmp(method,"handleinfo") == 0 ) + return(dilithium_handleinfo(txfee,cp,params)); + else if ( strcmp(method,"sign") == 0 ) + return(dilithium_sign(txfee,cp,params)); + else if ( strcmp(method,"verify") == 0 ) + return(dilithium_verify(txfee,cp,params)); + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid dilithium method")); + result.push_back(Pair("method",method)); + return(result); + } + } +#endif + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","only sudoku supported for now")); + result.push_back(Pair("evalcode",(int)cp->evalcode)); + return(result); + } +} + UniValue CClib_info(struct CCcontract_info *cp) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i; char str[2]; @@ -55,6 +328,7 @@ UniValue CClib_info(struct CCcontract_info *cp) for (i=0; i= 128 ) obj.push_back(Pair("funcid",CClib_methods[i].funcid)); else @@ -63,7 +337,8 @@ UniValue CClib_info(struct CCcontract_info *cp) str[1] = 0; obj.push_back(Pair("funcid",str)); } - obj.push_back(Pair("name",CClib_methods[i].method)); + obj.push_back(Pair("name",CClib_methods[i].CCname)); + obj.push_back(Pair("method",CClib_methods[i].method)); obj.push_back(Pair("help",CClib_methods[i].help)); obj.push_back(Pair("params_required",CClib_methods[i].numrequiredargs)); obj.push_back(Pair("params_max",CClib_methods[i].maxargs)); @@ -73,32 +348,38 @@ UniValue CClib_info(struct CCcontract_info *cp) return(result); } -UniValue CClib(struct CCcontract_info *cp,char *method,cJSON *params) +UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr) { - UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; + UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; cJSON *params; +//printf("CClib params.(%s)\n",jsonstr!=0?jsonstr:""); for (i=0; ievalcode == CClib_methods[i].evalcode && strcmp(method,CClib_methods[i].method) == 0 ) { - result.push_back(Pair("result","success")); - result.push_back(Pair("method",CClib_methods[i].method)); - rawtx = CClib_rawtxgen(cp,CClib_methods[i].funcid,params); - result.push_back(Pair("rawtx",rawtx)); - return(result); + if ( cp->evalcode == EVAL_FAUCET2 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("method",CClib_methods[i].method)); + params = cJSON_Parse(jsonstr); + rawtx = CClib_rawtxgen(cp,CClib_methods[i].funcid,params); + free_json(params); + result.push_back(Pair("rawtx",rawtx)); + return(result); + } else return(CClib_method(cp,method,jsonstr)); } } result.push_back(Pair("result","error")); - result.push_back(Pair("method",CClib_methods[i].method)); + result.push_back(Pair("method",method)); result.push_back(Pair("error","method not found")); return(result); } -int64_t IsCClibvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +int64_t IsCClibvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr) { char destaddr[64]; if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cmpaddr) == 0 ) return(tx.vout[v].nValue); } return(0); @@ -123,7 +404,7 @@ bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction //fprintf(stderr,"vini.%d check hash and vout\n",i); if ( hashBlock == zerohash ) return eval->Invalid("cant faucet2 from mempool"); - if ( (assetoshis= IsCClibvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) + if ( (assetoshis= IsCClibvout(cp,vinTx,tx.vin[i].prevout.n,cp->unspendableCCaddr)) != 0 ) inputs += assetoshis; } } @@ -131,7 +412,7 @@ bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction for (i=0; iunspendableCCaddr)) != 0 ) outputs += assetoshis; } if ( inputs != outputs+FAUCET2SIZE+txfee ) @@ -146,6 +427,24 @@ bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; std::vector > txids; + if ( cp->evalcode != EVAL_FAUCET2 ) + { +#ifdef BUILD_ROGUE + return(rogue_validate(cp,height,eval,tx)); +#elif BUILD_CUSTOMCC + return(custom_validate(cp,height,eval,tx)); +#elif BUILD_GAMESCC + return(games_validate(cp,height,eval,tx)); +#else + if ( cp->evalcode == EVAL_SUDOKU ) + return(sudoku_validate(cp,height,eval,tx)); + else if ( cp->evalcode == EVAL_MUSIG ) + return(musig_validate(cp,height,eval,tx)); + else if ( cp->evalcode == EVAL_DILITHIUM ) + return(dilithium_validate(cp,height,eval,tx)); + else return eval->Invalid("invalid evalcode"); +#endif + } numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -170,7 +469,7 @@ bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C else { preventCCvouts = 1; - if ( IsCClibvout(cp,tx,0) != 0 ) + if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) != 0 ) { preventCCvouts++; i = 1; @@ -183,7 +482,7 @@ bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 ) return eval->Invalid("invalid faucetget txid"); Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); - SetCCtxids(txids,destaddr); + SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition()); for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) { //int height = it->first.blockHeight; @@ -202,24 +501,28 @@ bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C } } -int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs,char *cmpaddr,int32_t CCflag) { - char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0,txfee = 10000; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs != 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( it->second.satoshis < threshold ) + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f vs %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN,(double)threshold/COIN); + if ( it->second.satoshis < threshold || it->second.satoshis == txfee ) continue; - //char str[65]; fprintf(stderr,"check %s/v%d %.8f`\n",uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( (nValue= IsCClibvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( (nValue= IsCClibvout(cp,vintx,vout,cmpaddr)) >= 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -228,12 +531,36 @@ int64_t AddCClibInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK n++; if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; - } else fprintf(stderr,"nValue too small or already spent in mempool\n"); + } //else fprintf(stderr,"nValue %.8f too small or already spent in mempool\n",(double)nValue/COIN); } else fprintf(stderr,"couldnt get tx\n"); } return(totalinputs); } +int64_t AddCClibtxfee(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk) +{ + char coinaddr[64]; int64_t nValue,txfee = 10000; uint256 txid,hashBlock; CTransaction vintx; int32_t vout; + std::vector > unspentOutputs; + GetCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f vs %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN,(double)threshold/COIN); + if ( it->second.satoshis < txfee ) + continue; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( (nValue= IsCClibvout(cp,vintx,vout,coinaddr)) != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + return(it->second.satoshis); + } //else fprintf(stderr,"nValue %.8f too small or already spent in mempool\n",(double)nValue/COIN); + } else fprintf(stderr,"couldnt get tx\n"); + } + return(0); +} std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds) { @@ -251,21 +578,6 @@ std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds) return(""); } -/*UniValue FaucetInfo() -{ - UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey faucetpk; struct CCcontract_info *cp,C; int64_t funding; - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Faucet")); - cp = CCinit(&C,EVAL_FAUCET); - faucetpk = GetUnspendable(cp,0); - funding = AddFaucetInputs(cp,mtx,faucetpk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); - return(result); -}*/ - std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params) { CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -284,19 +596,19 @@ std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *para return(""); cclibpk = GetUnspendable(cp,0); mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddCClibInputs(cp,mtx,cclibpk,nValue+txfee,60)) > 0 ) + if ( (inputs= AddCClibInputs(cp,mtx,cclibpk,nValue+txfee,60,cp->unspendableCCaddr,1)) > 0 ) { if ( inputs > nValue ) CCchange = (inputs - nValue - txfee); if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_FIRSTUSER,CCchange,cclibpk)); + mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET2,CCchange,cclibpk)); mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); j = rand() & 0xfffffff; for (i=0; i<1000000; i++,j++) { tmpmtx = mtx; - rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FIRSTUSER << (uint8_t)'G' << j)); + rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_FAUCET2 << (uint8_t)'G' << j)); if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) { len >>= 1; @@ -315,3 +627,79 @@ std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *para } else fprintf(stderr,"cant find faucet inputs\n"); return(""); } + +UniValue cclib_error(UniValue &result,const char *errorstr) +{ + result.push_back(Pair("status","error")); + result.push_back(Pair("error",errorstr)); + return(result); +} + +uint256 juint256(cJSON *obj) +{ + uint256 tmp; bits256 t = jbits256(obj,0); + memcpy(&tmp,&t,sizeof(tmp)); + return(revuint256(tmp)); +} + +int32_t cclib_parsehash(uint8_t *hash32,cJSON *item,int32_t len) +{ + char *hexstr; + if ( (hexstr= jstr(item,0)) != 0 && is_hexstr(hexstr,0) == len*2 ) + { + decode_hex(hash32,len,hexstr); + return(0); + } else return(-1); +} + +#ifdef BUILD_ROGUE +#include "rogue_rpc.cpp" +#include "rogue/cursesd.c" +#include "rogue/vers.c" +#include "rogue/extern.c" +#include "rogue/armor.c" +#include "rogue/chase.c" +#include "rogue/command.c" +#include "rogue/daemon.c" +#include "rogue/daemons.c" +#include "rogue/fight.c" +#include "rogue/init.c" +#include "rogue/io.c" +#include "rogue/list.c" +#include "rogue/mach_dep.c" +#include "rogue/rogue.c" +#include "rogue/xcrypt.c" +#include "rogue/mdport.c" +#include "rogue/misc.c" +#include "rogue/monsters.c" +#include "rogue/move.c" +#include "rogue/new_level.c" +#include "rogue/options.c" +#include "rogue/pack.c" +#include "rogue/passages.c" +#include "rogue/potions.c" +#include "rogue/rings.c" +#include "rogue/rip.c" +#include "rogue/rooms.c" +#include "rogue/save.c" +#include "rogue/scrolls.c" +#include "rogue/state.c" +#include "rogue/sticks.c" +#include "rogue/things.c" +#include "rogue/weapons.c" +#include "rogue/wizard.c" + +#elif BUILD_CUSTOMCC +#include "customcc.cpp" + +#elif BUILD_GAMESCC +#include "rogue/cursesd.c" +#include "gamescc.cpp" + +#else +#include "sudoku.cpp" +#include "musig.cpp" +#include "dilithium.c" +//#include "../secp256k1/src/modules/musig/example.c" +#endif + diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index 6cd379eed..254f7e3a5 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -62,6 +62,8 @@ Possible third iteration: // start of consensus code +#define CC_MARKER_VALUE 10000 + int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v) { char destaddr[65],channeladdr[65],tokenschanneladdr[65]; @@ -92,26 +94,29 @@ int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,C CScript EncodeChannelsOpRet(uint8_t funcid,uint256 tokenid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain) { CScript opret; uint8_t evalcode = EVAL_CHANNELS; + vscript_t vopret; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain); + vopret = E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain); if (tokenid!=zeroid) { std::vector pks; pks.push_back(srcpub); pks.push_back(destpub); - return(EncodeTokenOpRet(tokenid,pks,opret)); + return(EncodeTokenOpRet(tokenid,pks, std::make_pair(OPRETID_CHANNELSDATA, vopret))); } + opret << OP_RETURN << vopret; return(opret); } uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain) { - std::vector vopret; uint8_t *script,e,f,tokenevalcode; - std::vector pubkeys; std::vector vOpretExtra; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; + std::vector pubkeys; - if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,vOpretExtra)!=0 && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { - if (!E_UNMARSHAL(vOpretExtra, { ss >> vopret; })) return (0); + vopret=vOpretExtra; } else GetOpReturnData(scriptPubKey, vopret); if ( vopret.size() > 2 ) @@ -123,8 +128,8 @@ uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint2 { return(f); } - } else fprintf(stderr,"script[0] %02x != EVAL_CHANNELS\n",script[0]); - } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "script[0] " << script[0] << " != EVAL_CHANNELS" << std::endl); + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "not enough opret.[" << vopret.size() << "]" << std::endl); return(0); } @@ -164,7 +169,7 @@ bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti } if ( inputs != outputs ) { - fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); + LOGSTREAM("channelscc",CCLOG_INFO, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl); return eval->Invalid("mismatched inputs != outputs"); } else return (true); @@ -180,7 +185,7 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval; uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid; - uint8_t funcid,hash[32],hashdest[32]; + uint8_t funcid,hash[32],hashdest[32]; char channeladdress[65],srcmarker[65],destmarker[65],destaddr[65],srcaddr[65],desttokensaddr[65],srctokensaddr[65]; int64_t p2,param2,payment; CPubKey srcpub, destpub; CTransaction channelOpenTx,channelCloseTx,prevTx; @@ -199,9 +204,20 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & else { txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); + memcpy(hash,&txid,sizeof(hash)); if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3)) != 0) { + if (myGetTransaction(opentxid,channelOpenTx,hashblock)== 0) + return eval->Invalid("invalid channelopen tx!"); + else if ((numvouts=channelOpenTx.vout.size()) > 0 && (DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 'O') + return eval->Invalid("invalid channelopen OP_RETURN data!"); + GetCCaddress1of2(cp,channeladdress,srcpub,destpub); + GetCCaddress(cp,srcmarker,srcpub); + GetCCaddress(cp,destmarker,destpub); + Getscriptaddress(srcaddr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG); + Getscriptaddress(destaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); + _GetCCaddress(srctokensaddr,EVAL_TOKENS,srcpub); + _GetCCaddress(desttokensaddr,EVAL_TOKENS,destpub); switch ( funcid ) { case 'O': @@ -222,56 +238,53 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.3: normal output of payment amount to receiver pubkey //vout.n-2: normal change //vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelPayment!"); + return eval->Invalid("vin.0 is normal for channelpayment!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelPayment!"); + return eval->Invalid("vin.1 is CC for channelpayment!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelPayment!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition()==0 ) - return eval->Invalid("vout.0 is CC for channelPayment!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelPayment (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,2)==0 ) - return eval->Invalid("vout.2 is CC for channelPayment (marker to dstPub)!"); - else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.3 is CC for channelPayment!"); - else if ( tokenid==zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("vout.3 is normal for channelPayment!"); - else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[3].nValue,destpub).scriptPubKey) - return eval->Invalid("payment funds do not go to receiver!"); - else if ( tokenid==zeroid && tx.vout[3].scriptPubKey!=CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG) - return eval->Invalid("payment funds do not go to receiver!"); + return eval->Invalid("vin.2 is CC for channelpayment!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelpayment!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[3],1,desttokensaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is CC or invalid amount or invalid receiver for channelpayment!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[3],0,destaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is normal or invalid amount or invalid receiver for channelpayment!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); else - { - if (myGetTransaction(opentxid,channelOpenTx,hashblock) != 0) + { + endiancpy(hash, (uint8_t * ) & param3, 32); + for (i = 0; i < numpayments-param1; i++) { - if ((numvouts=channelOpenTx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 0 && funcid!='O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - endiancpy(hash, (uint8_t * ) & param3, 32); - for (i = 0; i < numpayments-param1; i++) - { - vcalc_sha256(0, hashdest, hash, 32); - memcpy(hash, hashdest, 32); - } - endiancpy((uint8_t*)&genhashchain,hashdest,32); - if (hashchain!=genhashchain) - return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); - else if (tx.vout[3].nValue != param2*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); + vcalc_sha256(0, hashdest, hash, 32); + memcpy(hash, hashdest, 32); } + endiancpy((uint8_t*)&genhashchain,hashdest,32); + if (hashchain!=genhashchain) + return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); + else if (tx.vout[3].nValue != param2*payment) + return eval->Invalid("vout amount does not match number_of_payments*payment!"); if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelpayment!"); else if (param1+param2!=p1) return eval->Invalid("invalid payment depth!"); else if (tx.vout[3].nValue > prevTx.vout[0].nValue) @@ -287,37 +300,37 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.1: CC vout marker to senders pubKey //vout.2: CC vout marker to receiver pubkey //vout.n-2: normal change - //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey 0 0 0 - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey numpayments payment 0 + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelClose!"); + return eval->Invalid("vin.0 is normal for channelclose!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelClose!"); + return eval->Invalid("vin.1 is CC for channelclose!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelClose!"); - else if ( IsChannelsvout(cp,tx,srcpub,destpub,0)==0 ) - return eval->Invalid("vout.0 is CC for channelClose!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelClose (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,2)==0 ) - return eval->Invalid("vout.2 is CC for channelClose (marker to dstPub)!"); + return eval->Invalid("vin.2 is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,0)==0 ) + return eval->Invalid("vout.0 is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelclose!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelclose!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); - else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - return eval->Invalid("invalid open txid!"); - else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - else if (tx.vout[0].nValue != param1*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelclose!"); else if (tx.vout[0].nValue != prevTx.vout[0].nValue) return eval->Invalid("invalid CC amount, amount must match funds in channel"); } @@ -331,50 +344,40 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.2: normal output of CC input to senders pubkey //vout.n-2: normal change //vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if (komodo_txnotarizedconfirmed(param3) == 0) return eval->Invalid("channelClose is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelRefund!"); + return eval->Invalid("vin.0 is normal for channelrefund!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelRefund!"); + return eval->Invalid("vin.1 is CC for channelrefund!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelRefund!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,0)==0 ) - return eval->Invalid("vout.0 is CC for channelRefund (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelRefund (marker to dstPub)!"); - else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.2 is CC for channelPayment!"); - else if ( tokenid==zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("vout.2 is normal for channelPayment!"); - else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[2].nValue,srcpub).scriptPubKey) - return eval->Invalid("payment funds do not go to sender!"); - else if ( tokenid==zeroid && tx.vout[2].scriptPubKey!=CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG) - return eval->Invalid("payment funds do not go to sender!"); + return eval->Invalid("vin.2 is CC for channelrefund!"); + else if ( ConstrainVout(tx.vout[0],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.0 is CC marker to srcpub or invalid amount for channelrefund!"); + else if ( ConstrainVout(tx.vout[1],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to destpub or invalid amount for channelrefund!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[2],1,srctokensaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is CC or invalid amount or invalid receiver for channelrefund!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[2],0,srcaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is normal or invalid amount or invalid receiver for channelrefund!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); - else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - return eval->Invalid("invalid open txid!"); - else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - else if (myGetTransaction(param3,channelCloseTx,hashblock) == 0) - return eval->Invalid("invalid close txid!"); - else if ((numvouts=channelCloseTx.vout.size()) > 0 && DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, param1, param2, param3) != 'C') - return eval->Invalid("invalid channelclose OP_RETURN data!"); - else if (tmp_txid!=opentxid) - return eval->Invalid("invalid close tx, opentxid do not match on close and refund!"); - else if (tx.vout[2].nValue != param1*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[0].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelrefund!"); else if (tx.vout[2].nValue != prevTx.vout[0].nValue) return eval->Invalid("invalid amount, refund amount and funds in channel must match!"); } @@ -387,8 +390,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & else return eval->Invalid("unexpected channels missing funcid"); retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) - fprintf(stderr,"Channel tx validated\n"); - else fprintf(stderr,"Channel tx invalid\n"); + LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl); + else fprintf(stderr,"Channels tx invalid\n"); return(retval); } } @@ -408,11 +411,11 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C { if (tokenid!=zeroid) GetTokensCCaddress1of2(cp,coinaddr,srcpub,destpub); else GetCCaddress1of2(cp,coinaddr,srcpub,destpub); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); } else { - fprintf(stderr,"invalid channel open txid\n"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << "invalid channel open txid" << std::endl); return 0; } if (srcpub==mypk) marker=1; @@ -430,7 +433,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C } } } - if (txid!=zeroid && myIsutxo_spentinmempool(txid,0) != 0) + if (txid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) != 0) { txid=zeroid; int32_t mindepth=CHANNELS_MAXPAYMENTS; @@ -455,7 +458,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C mtx.vin.push_back(CTxIn(txid,marker,CScript())); Myprivkey(myprivkey); if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,coinaddr); - else CCaddr1of2set(cp,srcpub,destpub,coinaddr); + else CCaddr1of2set(cp,srcpub,destpub,myprivkey,coinaddr); return totalinputs; } else return 0; @@ -470,7 +473,7 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS ) { CCerror = strprintf("invalid ChannelOpen param numpayments.%d max.%d payment.%lld\n",numpayments,CHANNELS_MAXPAYMENTS,(long long)payment); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } cp = CCinit(&C,EVAL_CHANNELS); @@ -481,11 +484,11 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 funds = numpayments * payment; if (tokenid!=zeroid) { - amount=AddNormalinputs(mtx,mypk,3*txfee,5); + amount=AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,5); tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64); } - else amount=AddNormalinputs(mtx,mypk,funds+3*txfee,64); - if (amount+tokens >= funds+2*txfee) + else amount=AddNormalinputs(mtx,mypk,funds+txfee+2*CC_MARKER_VALUE,64); + if (amount+tokens >= funds+txfee+2*CC_MARKER_VALUE) { hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); endiancpy(hash,(uint8_t *)&hentropy,32); @@ -497,13 +500,13 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 endiancpy((uint8_t *)&hashchain,hashdest,32); if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid && tokens>funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain))); } CCerror = strprintf("error adding funds"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -523,7 +526,7 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O') @@ -531,23 +534,29 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (mypk != srcpub && mypk != destpub) { CCerror = strprintf("this is not our channel"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } else if (amount % payment != 0 || amount 0) + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0) { @@ -559,12 +568,12 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (numpayments > prevdepth) { CCerror = strprintf("not enough funds in channel for that amount"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } else if (numpayments == 0) { CCerror = strprintf("invalid amount"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (secret!=zeroid) @@ -579,7 +588,7 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (gensecret!=hashchain) { CCerror = strprintf("invalid secret supplied"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } @@ -602,13 +611,13 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 else { CCerror = strprintf("invalid previous tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,srcpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,srcpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, destpub)); else mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG)); return (FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret))); @@ -616,12 +625,12 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -642,40 +651,46 @@ std::string ChannelClose(uint64_t txfee,uint256 opentxid) if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { CCerror = strprintf("invalid channel open tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (mypk != srcpub) { CCerror = strprintf("cannot close, you are not channel owner"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid))); } else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -696,48 +711,60 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) if (GetTransaction(closetxid,channelCloseTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel close txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C') { CCerror = strprintf("invalid channel close tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(closetxid)==false) + { + CCerror = strprintf("channelsclose tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (txid!=opentxid) { CCerror = strprintf("open and close txid are not from same channel"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { CCerror = strprintf("invalid channel open tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (mypk != srcpub) { CCerror = strprintf("cannot refund, you are not the channel owner"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if ((GetTransaction(prevtxid,prevTx,hashblock,false) != 0) && (numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0) { - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk)); else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid))); @@ -745,19 +772,19 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) else { CCerror = strprintf("previous tx is invalid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -770,7 +797,7 @@ UniValue ChannelsList() cp = CCinit(&C,EVAL_CHANNELS); mypk = pubkey2pk(Mypubkey()); GetCCaddress(cp,myCCaddr,mypk); - SetCCtxids(txids,myCCaddr); + SetCCtxids(txids,myCCaddr,true); result.push_back(Pair("result","success")); result.push_back(Pair("name","Channels List")); for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) @@ -778,7 +805,7 @@ UniValue ChannelsList() txid = it->first.txhash; vout = (int32_t)it->first.index; nValue = (int64_t)it->second; - if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) + if ( (vout == 1 || vout == 2) && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) { if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O') { @@ -821,7 +848,7 @@ UniValue ChannelsInfo(uint256 channeltxid) result.push_back(Pair("Denomination (satoshi)",i64tostr(param2))); result.push_back(Pair("Amount (satoshi)",i64tostr(param1*param2))); } - SetCCtxids(addressIndex,CCaddr); + SetCCtxids(addressIndex,CCaddr,true); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { if (GetTransaction(it->first.txhash,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) diff --git a/src/cc/customcc.cpp b/src/cc/customcc.cpp new file mode 100644 index 000000000..9f58c5c8b --- /dev/null +++ b/src/cc/customcc.cpp @@ -0,0 +1,88 @@ +/* + simple stub custom cc + + Just update the functions in this file, then from ~/komodo/src/cc + + ../komodo-cli -ac_name=CUSTOM stop + ./makecustom + ../komodod -ac_name=CUSTOM -ac_cclib=custom -ac_cc=2 ... + + The above will rebuild komodod and get it running again + */ + +CScript custom_opret(uint8_t funcid,CPubKey pk) +{ + CScript opret; uint8_t evalcode = EVAL_CUSTOM; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk); + return(opret); +} + +uint8_t custom_opretdecode(CPubKey &pk,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_CUSTOM ) + { + return(f); + } + return(0); +} + +UniValue custom_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +UniValue custom_func0(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back(Pair("result","success")); + result.push_back(Pair("message","just an example of an information returning rpc")); + return(result); +} + +// send yourself 1 coin to your CC address using normal utxo from your -pubkey + +UniValue custom_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); std::string rawtx; + UniValue result(UniValue::VOBJ); CPubKey mypk; int64_t amount = COIN; int32_t broadcastflag=0; + if ( txfee == 0 ) + txfee = CUSTOM_TXFEE; + mypk = pubkey2pk(Mypubkey()); + if ( AddNormalinputs(mtx,mypk,COIN+txfee,64) >= COIN+txfee ) // add utxo to mtx + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,mypk)); // make vout0 + // add opreturn, change is automatically added and tx is properly signed + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,custom_opret('1',mypk)); + return(custom_rawtxresult(result,rawtx,broadcastflag)); + } + return(result); +} + +bool custom_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + char expectedaddress[64]; CPubKey pk; + if ( tx.vout.size() != 2 ) // make sure the tx only has 2 outputs + return eval->Invalid("invalid number of vouts"); + else if ( custom_opretdecode(pk,tx.vout[1].scriptPubKey) != '1' ) // verify has opreturn + return eval->Invalid("invalid opreturn"); + GetCCaddress(cp,expectedaddress,pk); + if ( IsCClibvout(cp,tx,0,expectedaddress) == COIN ) // make sure amount and destination matches + return(true); + else return eval->Invalid("invalid vout0 amount"); +} + + diff --git a/src/cc/customcc.h b/src/cc/customcc.h new file mode 100644 index 000000000..436937391 --- /dev/null +++ b/src/cc/customcc.h @@ -0,0 +1,45 @@ +/* + to create a custom libcc.so: + + 1. change "func0" and "func1" to method names that fit your custom cc. Of course, you can create more functions by adding another entry to RPC_FUNCS. there is not any practical limit to the number of methods. + + 2. For each method make sure there is a UniValue function declaration and CUSTOM_DISPATCH has an if statement checking for it that calls the custom_func + + 3. write the actual custom_func0, custom_func1 and custom_validate in customcc.cpp + + 4. ./makecustom, which builds cclib.cpp with -DBUILD_CUSTOMCC and puts the libcc.so in ~/komodo/src and rebuilds komodod + + 5. launch your chain with -ac_cclib=customcc -ac_cc=2 + + */ + +std::string MYCCLIBNAME = (char *)"customcc"; + +#define EVAL_CUSTOM (EVAL_FAUCET2+1) +#define CUSTOM_TXFEE 10000 + +#define MYCCNAME "custom" + +#define RPC_FUNCS \ + { (char *)MYCCNAME, (char *)"func0", (char *)"", 1, 1, '0', EVAL_CUSTOM }, \ + { (char *)MYCCNAME, (char *)"func1", (char *)"", 0, 0, '1', EVAL_CUSTOM }, + +bool custom_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); +UniValue custom_func0(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue custom_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +#define CUSTOM_DISPATCH \ +if ( cp->evalcode == EVAL_CUSTOM ) \ +{ \ + if ( strcmp(method,"func0") == 0 ) \ + return(custom_func0(txfee,cp,params)); \ + else if ( strcmp(method,"func1") == 0 ) \ + return(custom_func1(txfee,cp,params)); \ + else \ + { \ + result.push_back(Pair("result","error")); \ + result.push_back(Pair("error","invalid customcc method")); \ + result.push_back(Pair("method",method)); \ + return(result); \ + } \ +} diff --git a/src/cc/dapps/cJSON.c b/src/cc/dapps/cJSON.c old mode 100755 new mode 100644 diff --git a/src/cc/dapps/dappstd.c b/src/cc/dapps/dappstd.c new file mode 100644 index 000000000..82366315c --- /dev/null +++ b/src/cc/dapps/dappstd.c @@ -0,0 +1,1119 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +// requires CHAINNAME and GAMEMAIN() to be #defined + +#include +#include +#include +#include +#include +#include +#include + +extern struct games_state globalR; +void *gamesiterate(struct games_state *rs); + +char USERPASS[8192]; uint16_t GAMES_PORT; +char Gametxidstr[67]; +char *clonestr(char *str); + +#define MAXSTR 1024 +char whoami[MAXSTR]; + +#define SMALLVAL 0.000000000000001 +#define SATOSHIDEN ((uint64_t)100000000L) +#define dstr(x) ((double)(x) / SATOSHIDEN) +#define KOMODO_ASSETCHAIN_MAXLEN 65 +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],IPADDRESS[100]; + +#ifndef _BITS256 +#define _BITS256 +union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; +typedef union _bits256 bits256; +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif // _MSC_VER +#endif + +double OS_milliseconds() +{ + struct timeval tv; double millis; +#ifdef __MINGW32__ + mingw_gettimeofday(&tv,NULL); +#else + gettimeofday(&tv,NULL); +#endif + millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); + //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); + return(millis); +} + +int32_t _unhex(char c) +{ + if ( c >= '0' && c <= '9' ) + return(c - '0'); + else if ( c >= 'a' && c <= 'f' ) + return(c - 'a' + 10); + else if ( c >= 'A' && c <= 'F' ) + return(c - 'A' + 10); + return(-1); +} + +int32_t is_hexstr(char *str,int32_t n) +{ + int32_t i; + if ( str == 0 || str[0] == 0 ) + return(0); + for (i=0; str[i]!=0; i++) + { + if ( n > 0 && i >= n ) + break; + if ( _unhex(str[i]) < 0 ) + break; + } + if ( n == 0 ) + return(i); + return(i == n); +} + +int32_t unhex(char c) +{ + int32_t hex; + if ( (hex= _unhex(c)) < 0 ) + { + //printf("unhex: illegal hexchar.(%c)\n",c); + } + return(hex); +} + +unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } + +int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex) +{ + int32_t adjust,i = 0; + //printf("decode.(%s)\n",hex); + if ( is_hexstr(hex,n) <= 0 ) + { + memset(bytes,0,n); + return(n); + } + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) + { + if ( n > 0 ) + { + bytes[0] = unhex(hex[0]); + printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); + } + bytes++; + hex++; + adjust = 1; + } else adjust = 0; + if ( n > 0 ) + { + for (i=0; i>4) & 0xf); + hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); + //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); + } + hexbytes[len*2] = 0; + //printf("len.%ld\n",len*2+1); + return((int32_t)len*2+1); +} + +char *bits256_str(char hexstr[65],bits256 x) +{ + init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); + return(hexstr); +} + +long _stripwhite(char *buf,int accept) +{ + int32_t i,j,c; + if ( buf == 0 || buf[0] == 0 ) + return(0); + for (i=j=0; buf[i]!=0; i++) + { + buf[j] = c = buf[i]; + if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) + j++; + } + buf[j] = 0; + return(j); +} + +char *parse_conf_line(char *line,char *field) +{ + line += strlen(field); + for (; *line!='='&&*line!=0; line++) + break; + if ( *line == 0 ) + return(0); + if ( *line == '=' ) + line++; + while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' ) + line[strlen(line)-1] = 0; + //printf("LINE.(%s)\n",line); + _stripwhite(line,0); + return(clonestr(line)); +} + +int32_t safecopy(char *dest,char *src,long len) +{ + int32_t i = -1; + if ( src != 0 && dest != 0 && src != dest ) + { + if ( dest != 0 ) + memset(dest,0,len); + for (i=0; i buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +uint8_t *OS_fileptr(long *allocsizep,char *fname) +{ + long filesize = 0; uint8_t *buf = 0; void *retptr; + *allocsizep = 0; + retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); + return((uint8_t *)retptr); +} + +struct MemoryStruct { char *memory; size_t size; }; +struct return_string { char *ptr; size_t len; }; + +// return data from the server +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) + + +/************************************************************************ + * + * Initialize the string handler so that it is thread safe + * + ************************************************************************/ + +void init_string(struct return_string *s) +{ + s->len = 0; + s->ptr = (char *)calloc(1,s->len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr,"init_string malloc() failed\n"); + exit(-1); + } + s->ptr[0] = '\0'; +} + +/************************************************************************ + * + * Use the "writer" to accumulate text until done + * + ************************************************************************/ + +size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s) +{ + size_t new_len = s->len + size*nmemb; + s->ptr = (char *)realloc(s->ptr,new_len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr, "accumulate realloc() failed\n"); + exit(-1); + } + memcpy(s->ptr+s->len,ptr,size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return(size * nmemb); +} + +/************************************************************************ + * + * return the current system time in milliseconds + * + ************************************************************************/ + +#define EXTRACT_BITCOIND_RESULT // if defined, ensures error is null and returns the "result" field +#ifdef EXTRACT_BITCOIND_RESULT + +/************************************************************************ + * + * perform post processing of the results + * + ************************************************************************/ + +char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params) +{ + long i,j,len; char *retstr = 0; cJSON *json,*result,*error; + //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + return(rpcstr); + } + json = cJSON_Parse(rpcstr); + if ( json == 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params); + free(rpcstr); + return(0); + } + result = cJSON_GetObjectItem(json,"result"); + error = cJSON_GetObjectItem(json,"error"); + if ( error != 0 && result != 0 ) + { + if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL ) + { + retstr = cJSON_Print(result); + len = strlen(retstr); + if ( retstr[0] == '"' && retstr[len-1] == '"' ) + { + for (i=1,j=0; itype&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr); + } + free(rpcstr); + } else retstr = rpcstr; + free_json(json); + //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr); + return(retstr); +} +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +/************************************************************************ + * + * perform the query + * + ************************************************************************/ + +char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params) +{ + static int didinit,count,count2; static double elapsedsum,elapsedsum2; + struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle; + char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime; + if ( didinit == 0 ) + { + didinit = 1; + curl_global_init(CURL_GLOBAL_ALL); //init the curl session + } + numretries = 0; + if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) + specialcase = 1; + else specialcase = 0; + if ( url[0] == 0 ) + strcpy(url,"http://127.0.0.1:7876/nxt"); + if ( specialcase != 0 && 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params); +try_again: + if ( retstrp != 0 ) + *retstrp = 0; + starttime = OS_milliseconds(); + curl_handle = curl_easy_init(); + init_string(&s); + headers = curl_slist_append(0,"Expect:"); + + curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl_handle,CURLOPT_URL, url); + curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function + curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback + curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash + curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback + if ( strncmp(url,"https",5) == 0 ) + { + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0); + } + if ( userpass != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass); + databuf = 0; + if ( params != 0 ) + { + if ( command != 0 && specialcase == 0 ) + { + len = strlen(params); + if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) { + bracket0 = bracket1 = (char *)""; + } + else + { + bracket0 = (char *)"["; + bracket1 = (char *)"]"; + } + + databuf = (char *)malloc(256 + strlen(command) + strlen(params)); + sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1); + //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf); + // + } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params); + curl_easy_setopt(curl_handle,CURLOPT_POST,1L); + if ( databuf != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf); + else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params); + } + //laststart = milliseconds(); + res = curl_easy_perform(curl_handle); + curl_slist_free_all(headers); + curl_easy_cleanup(curl_handle); + if ( databuf != 0 ) // clean up temporary buffer + { + free(databuf); + databuf = 0; + } + if ( res != CURLE_OK ) + { + numretries++; + if ( specialcase != 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); + free(s.ptr); + return(0); + } + else if ( numretries >= 1 ) + { + //printf("Maximum number of retries exceeded!\n"); + free(s.ptr); + return(0); + } + if ( (rand() % 1000) == 0 ) + printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries); + free(s.ptr); + sleep((1< (%s)\n",params,s.ptr); + count2++; + elapsedsum2 += (OS_milliseconds() - starttime); + if ( (count2 % 10000) == 0) + printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command); + return(s.ptr); + } + } + printf("bitcoind_RPC: impossible case\n"); + free(s.ptr); + return(0); +} + +static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data) +{ + size_t realsize = (size * nmemb); + struct MemoryStruct *mem = (struct MemoryStruct *)data; + mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1)); + if ( mem->memory != 0 ) + { + if ( ptr != 0 ) + memcpy(&(mem->memory[mem->size]),ptr,realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + //printf("got %d bytes\n",(int32_t)(size*nmemb)); + return(realsize); +} + +char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3) +{ + struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0; + if ( (cHandle= *cHandlep) == NULL ) + *cHandlep = cHandle = curl_easy_init(); + else curl_easy_reset(cHandle); + //#ifdef DEBUG + //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1); + //#endif + curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0); + //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1); + curl_easy_setopt(cHandle,CURLOPT_URL,url); + curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10); + if ( userpass != 0 && userpass[0] != 0 ) + curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass); + if ( postfields != 0 && postfields[0] != 0 ) + { + curl_easy_setopt(cHandle,CURLOPT_POST,1); + curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields); + } + if ( hdr0 != NULL && hdr0[0] != 0 ) + { + //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:""); + headers = curl_slist_append(headers,hdr0); + if ( hdr1 != 0 && hdr1[0] != 0 ) + headers = curl_slist_append(headers,hdr1); + if ( hdr2 != 0 && hdr2[0] != 0 ) + headers = curl_slist_append(headers,hdr2); + if ( hdr3 != 0 && hdr3[0] != 0 ) + headers = curl_slist_append(headers,hdr3); + } //headers = curl_slist_append(0,"Expect:"); + if ( headers != 0 ) + curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers); + //res = curl_easy_perform(cHandle); + memset(&chunk,0,sizeof(chunk)); + curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback); + curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk); + curl_easy_perform(cHandle); + curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code); + if ( headers != 0 ) + curl_slist_free_all(headers); + if ( code != 200 ) + printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory); + return(chunk.memory); +} + +uint16_t _komodo_userpass(char *username, char *password, FILE *fp) +{ + char *rpcuser,*rpcpassword,*str,*ipaddress,line[8192]; uint16_t port = 0; + rpcuser = rpcpassword = 0; + username[0] = password[0] = 0; + while ( fgets(line,sizeof(line),fp) != 0 ) + { + if ( line[0] == '#' ) + continue; + //printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword")); + if ( (str= strstr(line,(char *)"rpcuser")) != 0 ) + rpcuser = parse_conf_line(str,(char *)"rpcuser"); + else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 ) + rpcpassword = parse_conf_line(str,(char *)"rpcpassword"); + else if ( (str= strstr(line,(char *)"rpcport")) != 0 ) + { + port = atoi(parse_conf_line(str,(char *)"rpcport")); + //fprintf(stderr,"rpcport.%u in file\n",port); + } + else if ( (str= strstr(line,(char *)"ipaddress")) != 0 ) + { + ipaddress = parse_conf_line(str,(char *)"ipaddress"); + strcpy(IPADDRESS,ipaddress); + } + } + if ( rpcuser != 0 && rpcpassword != 0 ) + { + strcpy(username,rpcuser); + strcpy(password,rpcpassword); + } + //printf("rpcuser.(%s) rpcpassword.(%s) %u ipaddress.%s\n",rpcuser,rpcpassword,port,ipaddress); + if ( rpcuser != 0 ) + free(rpcuser); + if ( rpcpassword != 0 ) + free(rpcpassword); + return(port); +} + +uint16_t komodo_userpass(char *userpass,char *symbol) +{ + FILE *fp; uint16_t port = 0; char fname[512],username[512],password[512],confname[KOMODO_ASSETCHAIN_MAXLEN]; + userpass[0] = 0; + if ( strcmp("KMD",symbol) == 0 ) + { +#ifdef __APPLE__ + sprintf(confname,"Komodo.conf"); +#else + sprintf(confname,"komodo.conf"); +#endif + } + else sprintf(confname,"%s.conf",symbol); + //komodo_statefname(fname,symbol,confname); + if ( (fp= fopen(confname,"rb")) != 0 ) + { + port = _komodo_userpass(username,password,fp); + sprintf(userpass,"%s:%s",username,password); + if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) + strcpy(USERPASS,userpass); + fclose(fp); + } + return(port); +} + +#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) + +char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) +{ + //static void *cHandle; + char url[512],*retstr=0,*retstr2=0,postdata[8192]; + if ( params == 0 || params[0] == 0 ) + params = (char *)"[]"; + if ( strlen(params) < sizeof(postdata)-128 ) + { + sprintf(url,(char *)"http://%s:%u",IPADDRESS,port); + sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); + //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,USERPASS); + retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); + //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); + } + return(retstr2); +} + +int32_t games_sendrawtransaction(char *rawtx) +{ + char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; + params = (char *)malloc(strlen(rawtx) + 16); + sprintf(params,"[\"%s\"]",rawtx); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"sendrawtransaction",params,GAMES_PORT)) != 0 ) + { + if ( 0 ) // causes 4th level crash + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("games.sendlog","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) + retval = 0; + } + free_json(retjson); + } + + /* log sendrawtx result in file */ + + /* + FILE *debug_file; + debug_file = fopen("tx_debug.log", "a"); + fprintf(debug_file, "%s\n", retstr); + fflush(debug_file); + fclose(debug_file); + */ + + free(retstr); + } + free(params); + return(retval); +} + +int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,gamesevent *keystrokes,int32_t num) +{ + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx; int32_t i,len,retflag = -1; cJSON *retjson,*resobj; + if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) + { + if ( rs->keystrokeshex != 0 ) + { + if ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + if ( waitflag == 0 ) + return(0); + else if ( 0 ) + { + while ( games_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"pre-rebroadcast\n"); + sleep(10); + } + } + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + memset(hexstr,0,sizeof(hexstr)); + for (i=0; ikeystrokeshex != 0 ) + free(rs->keystrokeshex); + if ( (errstr= jstr(resobj,(char *)"error")) == 0 ) + { + rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); + strcpy(rs->keystrokeshex,rawtx); + retflag = 1; + } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); + //fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); + } + free_json(retjson); + } + free(retstr); + } + } + return(retflag); +} + +int32_t gamesfname(char *fname,uint64_t seed,int32_t counter) +{ + sprintf(fname,"%s.%llu.%d",GAMENAME,(long long)seed,counter); + return(0); +} + +int32_t flushkeystrokes_local(struct games_state *rs,int32_t waitflag) +{ +#ifdef STANDALONE + char fname[1024]; FILE *fp; int32_t i,retflag = -1; + rs->counter++; + gamesfname(fname,rs->origseed,rs->counter); + if ( (fp= fopen(fname,"wb")) != 0 ) + { + if ( fwrite(rs->buffered,sizeof(*rs->buffered),rs->num,fp) == rs->num ) + { + rs->num = 0; + retflag = 0; + fclose(fp); + gamesfname(fname,rs->origseed,rs->counter+1); + if ( (fp= fopen(fname,"wb")) != 0 ) // truncate next file + fclose(fp); + //fprintf(stderr,"savefile <- %s retflag.%d\n",fname,retflag); + //} + } else fprintf(stderr,"error writing (%s)\n",fname); + } else fprintf(stderr,"error creating (%s)\n",fname); + return(retflag); +#else + return(0); +#endif +} + +#ifndef STANDALONE +// stubs for inside daemon + +int32_t games_progress(struct games_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +{ + return(0); +} + +int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) +{ + return(-1); +} +#endif + +int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag) +{ + if ( rs->num > 0 ) + { + if ( games_progress(rs,waitflag,rs->origseed,rs->buffered,rs->num) > 0 ) + { + flushkeystrokes_local(rs,waitflag); + memset(rs->buffered,0,sizeof(rs->buffered)); + } + } + return(0); +} + +void gamesbailout(struct games_state *rs) +{ + flushkeystrokes(rs,1); +} + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +long get_filesize(FILE *fp) +{ + long fsize,fpos = ftell(fp); + fseek(fp,0,SEEK_END); + fsize = ftell(fp); + fseek(fp,fpos,SEEK_SET); + return(fsize); +} + +gamesevent *games_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) +{ + char fname[1024]; gamesevent *keystrokes = 0; FILE *fp; long fsize; int32_t i,num = 0; + *numkeysp = 0; + while ( 1 ) + { + gamesfname(fname,seed,counter); + //printf("check (%s)\n",fname); + if ( (fp= fopen(fname,"rb")) == 0 ) + break; + if ( (fsize= get_filesize(fp)) <= 0 ) + { + fclose(fp); + //printf("fsize.%ld\n",fsize); + break; + } + if ( (keystrokes= (gamesevent *)realloc(keystrokes,sizeof(*keystrokes)*num+fsize)) == 0 ) + { + fprintf(stderr,"error reallocating keystrokes\n"); + fclose(fp); + return(0); + } + if ( fread(&keystrokes[num],1,fsize,fp) != fsize ) + { + fprintf(stderr,"error reading keystrokes from (%s)\n",fname); + fclose(fp); + free(keystrokes); + return(0); + } + fclose(fp); + num += (int32_t)(fsize / sizeof(gamesevent)); + //for (i=0; i 0 ) + { + sprintf(fname,"%s.%llu.player",GAMENAME,(long long)seed); + if ( (fp=fopen(fname,"rb")) != 0 ) + { + if ( fread(&P,1,sizeof(P),fp) > 0 ) + { + //printf("max size player\n"); + player = &P; + } + fclose(fp); + } + games_replay2(0,seed,keystrokes,num,player,sleeptime); + mvaddstr(LINES - 2, 0, (char *)"replay completed"); + endwin(); + games_exit(); + } + if ( keystrokes != 0 ) + free(keystrokes); + return(num); +} + +int32_t games_setplayerdata(struct games_state *rs,char *gametxidstr) +{ + char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; + if ( rs->guiflag == 0 ) + return(-1); + if ( gametxidstr == 0 || *gametxidstr == 0 ) + return(retval); + if ( 0 ) + { + sprintf(fname,"%s.gameinfo",gametxidstr); + sprintf(cmd,"./komodo-cli -ac_name=%s cclib gameinfo 17 \\\"[%%22%s%%22]\\\" > %s",ASSETCHAINS_SYMBOL,gametxidstr,fname); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + else filestr = (char *)OS_fileptr(&allocsize,fname); + } + else + { + sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); + filestr = komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT); + } + if ( filestr != 0 ) + { + if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,(char *)"result")) != 0 ) + { + //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); + if ( (array= jarray(&n,resultjson,(char *)"players")) != 0 ) + { + for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); + fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); + rs->restoring = 1; + } + } + } + } + } + free_json(retjson); + } + free(filestr); + } + return(retval); +} + +#ifdef _WIN32 +#ifdef _MSC_VER +__inline int msver(void) { + switch (_MSC_VER) { + case 1500: return 2008; + case 1600: return 2010; + case 1700: return 2012; + case 1800: return 2013; + case 1900: return 2015; + //case 1910: return 2017; + default: return (_MSC_VER / 100); + } +} + +static inline bool is_x64(void) { +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) + return 1; +#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) + return 1; +#else + return 0; +#endif +} + +#define BUILD_DATE __DATE__ " " __TIME__ +#endif // _WIN32 +#endif // _MSC_VER + +int main(int argc, char **argv) +{ + uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; +#ifdef _WIN32 +#ifdef _MSC_VER + printf("*** games for Windows [ Build %s ] ***\n", BUILD_DATE); + const char* arch = is_x64() ? "64-bits" : "32-bits"; + printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); +#endif +#endif + + for (i=j=0; argv[0][i]!=0&&j buflen ) @@ -312,22 +312,20 @@ uint64_t get_btcusd() char *REFCOIN_CLI; -cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2,char *arg3) +cJSON *get_cli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2,char *arg3) { long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[256]; sprintf(fname,"/tmp/oraclefeed.%s",method); if ( acname[0] != 0 ) { - if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) - printf("unexpected: refcoin.(%s) acname.(%s)\n",refcoin,acname); - sprintf(cmdstr,"./komodo-cli -ac_name=%s %s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,arg3,fname); + if ( refcoin[0] == 0 ) + printf("must supply reference coin\n"); + sprintf(cmdstr,"./komodo-cli -ac_name=%s %s %s %s %s %s > %s 2>/tmp/oraclefeed.error\n",acname,method,arg0,arg1,arg2,arg3,fname); } - else if ( strcmp(refcoin,"KMD") == 0 ) - sprintf(cmdstr,"./komodo-cli %s %s %s %s %s > %s\n",method,arg0,arg1,arg2,arg3,fname); else if ( REFCOIN_CLI != 0 && REFCOIN_CLI[0] != 0 ) { - sprintf(cmdstr,"%s %s %s %s %s %s > %s\n",REFCOIN_CLI,method,arg0,arg1,arg2,arg3,fname); - printf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr); + sprintf(cmdstr,"%s %s %s %s %s %s > %s 2>/tmp/oraclefeed.error\n",REFCOIN_CLI,method,arg0,arg1,arg2,arg3,fname); + //printf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr); } #ifdef TESTMODE fprintf(stderr,"cmd: %s\n",cmdstr); @@ -340,22 +338,24 @@ cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char #ifdef TESTMODE fprintf(stderr,"jsonstr.(%s)\n",jsonstr); #endif // TESTMODE - if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) + if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0) *retstrp = jsonstr; else free(jsonstr); } + else if ( (jsonstr= filestr(&fsize,"/tmp/oraclefeed.error")) != 0 ) + *retstrp = jsonstr; return(retjson); } -bits256 komodobroadcast(char *refcoin,char *acname,cJSON *hexjson) +bits256 broadcasttx(char *refcoin,char *acname,cJSON *hexjson) { char *hexstr,*retstr,str[65]; cJSON *retjson; bits256 txid; memset(txid.bytes,0,sizeof(txid)); if ( (hexstr= jstr(hexjson,"hex")) != 0 ) { - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendrawtransaction",hexstr,"","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"sendrawtransaction",hexstr,"","","")) != 0 ) { - //fprintf(stderr,"broadcast.(%s)\n",jprint(retjson,0)); + if (strcmp("error",jstr(retjson,"result"))==0) printf("%s\n",jstr(retjson,"error")); free_json(retjson); } else if ( retstr != 0 ) @@ -372,36 +372,14 @@ bits256 komodobroadcast(char *refcoin,char *acname,cJSON *hexjson) return(txid); } -bits256 sendtoaddress(char *refcoin,char *acname,char *destaddr,int64_t satoshis) -{ - char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; - memset(txid.bytes,0,sizeof(txid)); - sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendtoaddress",destaddr,numstr,"","")) != 0 ) - { - fprintf(stderr,"unexpected sendrawtransaction json.(%s)\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - if ( strlen(retstr) >= 64 ) - { - retstr[64] = 0; - decode_hex(txid.bytes,32,retstr); - } - fprintf(stderr,"sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); - free(retstr); - } - return(txid); -} - int32_t get_coinheight(char *refcoin,char *acname) { cJSON *retjson; char *retstr; int32_t height=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockchaininfo","","","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"getblockchaininfo","","","","")) != 0 ) { height = jint(retjson,"blocks"); free_json(retjson); + } else if ( retstr != 0 ) { @@ -416,7 +394,7 @@ bits256 get_coinblockhash(char *refcoin,char *acname,int32_t height) cJSON *retjson; char *retstr,heightstr[32]; bits256 hash; memset(hash.bytes,0,sizeof(hash)); sprintf(heightstr,"%d",height); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","")) != 0 ) { fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0)); free_json(retjson); @@ -437,7 +415,7 @@ bits256 get_coinmerkleroot(char *refcoin,char *acname,bits256 blockhash) { cJSON *retjson; char *retstr,str[65]; bits256 merkleroot; memset(merkleroot.bytes,0,sizeof(merkleroot)); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","")) != 0 ) { merkleroot = jbits256(retjson,"merkleroot"); //fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot)); @@ -472,10 +450,12 @@ int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *m return(0); } -cJSON *get_gatewayspending(char *refcoin,char *acname,char *bindtxidstr) +cJSON *get_gatewayspending(int8_t type,char *refcoin,char *acname,char *bindtxidstr) { - cJSON *retjson; char *retstr; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewayspending",bindtxidstr,refcoin,"","")) != 0 ) + cJSON *retjson; char *retstr; char function[64]; + if (type==0) sprintf(function,"%s","gatewayspendingwithdraws"); + else if (type==1) sprintf(function,"%s","importgatewaypendingwithdraws"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,refcoin,"","")) != 0 ) { //printf("pending.(%s)\n",jprint(retjson,0)); return(retjson); @@ -488,10 +468,13 @@ cJSON *get_gatewayspending(char *refcoin,char *acname,char *bindtxidstr) return(0); } -cJSON *get_gatewaysprocessed(char *refcoin,char *acname,char *bindtxidstr) +cJSON *get_gatewaysprocessed(int8_t type,char *refcoin,char *acname,char *bindtxidstr) { cJSON *retjson; char *retstr; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewaysprocessed",bindtxidstr,refcoin,"","")) != 0 ) + char function[64]; + if (type==0) sprintf(function,"%s","gatewaysprocessed"); + else if (type==1) sprintf(function,"%s","importgatewayprocessed"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,refcoin,"","")) != 0 ) { //printf("pending.(%s)\n",jprint(retjson,0)); return(retjson); @@ -507,7 +490,7 @@ cJSON *get_gatewaysprocessed(char *refcoin,char *acname,char *bindtxidstr) cJSON *get_rawmempool(char *refcoin,char *acname) { cJSON *retjson; char *retstr; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawmempool","","","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"getrawmempool","","","","")) != 0 ) { //printf("mempool.(%s)\n",jprint(retjson,0)); return(retjson); @@ -526,14 +509,14 @@ cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr) if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) printf("warning: assumes %s has addressindex enabled\n",refcoin); sprintf(jsonbuf,"{\\\"addresses\\\":[\\\"%s\\\"]}",coinaddr); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getaddressutxos",jsonbuf,"","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"getaddressutxos",jsonbuf,"","","")) != 0 ) { //printf("addressutxos.(%s)\n",jprint(retjson,0)); return(retjson); } else if ( retstr != 0 ) { - fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr); + //fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr); free(retstr); } return(0); @@ -542,7 +525,7 @@ cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr) cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid) { cJSON *retjson; char *retstr,str[65]; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","","")) != 0 ) { return(retjson); } @@ -557,7 +540,7 @@ cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid) int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* compare) { cJSON *retjson; char *retstr; int32_t res=0; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","")) != 0 ) { if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1; free_json(retjson); @@ -576,7 +559,7 @@ void importaddress(char *refcoin,char *acname,char *depositaddr, char *label,int cJSON *retjson; char *retstr; char rescanstr[10]; if (rescan) strcpy(rescanstr,"true"); else strcpy(rescanstr,"false"); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"importaddress",depositaddr,label,rescanstr,"")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"importaddress",depositaddr,label,rescanstr,"")) != 0 ) { printf("importaddress.(%s)\n",jprint(retjson,0)); free_json(retjson); @@ -588,19 +571,20 @@ void importaddress(char *refcoin,char *acname,char *depositaddr, char *label,int } } -void addmultisigaddress(char *refcoin,char *acname,int32_t M, char *pubkeys,char *bindtxidstr) +void addmultisigaddress(char *refcoin,char *acname,int32_t M, char *pubkeys) { - cJSON *retjson; char *retstr,Mstr[10],tmp[128]; + cJSON *retjson; char *retstr,Mstr[10],addr[64]; sprintf(Mstr,"%d",M); - sprintf(tmp,"\"%s\"",bindtxidstr); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"addmultisigaddress",Mstr,pubkeys,tmp,"")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"addmultisigaddress",Mstr,pubkeys,"","")) != 0 ) { fprintf(stderr,"unexpected addmultisigaddress json.(%s)\n",jprint(retjson,0)); free(retstr); } else if ( retstr != 0 ) { + sprintf(addr,"\"%s\"",retstr); + get_cli(refcoin,&retstr,acname,"importaddress",addr,"\"\"","false",""); printf("addmultisigaddress.(%s)\n",retstr); free_json(retjson); } @@ -641,12 +625,11 @@ char *createrawtx(char *refcoin,char *acname,char *depositaddr,char *withdrawadd else txfee = 10000; if ( satoshis < txfee ) { - printf("createrawtx satoshis %.8f < txfee %.8f\n",(double)satoshis/SATOSHIDEN,(double)txfee/SATOSHIDEN); + printf("createrawtx: satoshis %.8f < txfee %.8f\n",(double)satoshis/SATOSHIDEN,(double)txfee/SATOSHIDEN); return(0); } - satoshis -= txfee; sprintf(array,"\'[\"%s\"]\'",depositaddr); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"listunspent","1","99999999",array,"")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"listunspent","1","99999999",array,"")) != 0 ) { //createrawtransaction [{"txid":"id","vout":n},...] {"address":amount,...} if ( (vins= getinputarray(&total,retjson,satoshis)) != 0 ) @@ -667,7 +650,7 @@ char *createrawtx(char *refcoin,char *acname,char *depositaddr,char *withdrawadd char *argB=malloc(sizeof(char) * (strlen(tmpB)+3)); sprintf(argA,"\'%s\'",tmpA); sprintf(argB,"\'%s\'",tmpB); - if ( (retjson2= get_komodocli(refcoin,&txstr,acname,"createrawtransaction",argA,argB,"","")) != 0 ) + if ( (retjson2= get_cli(refcoin,&txstr,acname,"createrawtransaction",argA,argB,"","")) != 0 ) { printf("createrawtx: unexpected JSON2.(%s)\n",jprint(retjson2,0)); free_json(retjson2); @@ -680,7 +663,7 @@ char *createrawtx(char *refcoin,char *acname,char *depositaddr,char *withdrawadd free(argB); } else printf("not enough funds to create withdraw tx\n"); - } + } free_json(retjson); } else if ( retstr != 0 ) @@ -692,14 +675,14 @@ char *createrawtx(char *refcoin,char *acname,char *depositaddr,char *withdrawadd return(txstr); } -cJSON *addsignature(char *refcoin,char *acname,char *rawtx) +cJSON *addsignature(char *refcoin,char *acname,char *rawtx, int M) { char *retstr,*hexstr; cJSON *retjson; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"signrawtransaction",rawtx,"","","")) != 0 ) + if ( (retjson= get_cli(refcoin,&retstr,acname,"signrawtransaction",rawtx,"","","")) != 0 ) { if ( is_cJSON_True(jobj(retjson,"complete")) != 0 ) return(retjson); - else if ( (hexstr= jstr(retjson,"hex")) != 0 && strlen(hexstr) > strlen(rawtx) ) + else if ((hexstr=jstr(retjson,"hex"))!= 0 && strlen(hexstr) > strlen(rawtx) + (M*2) + 1) { jaddnum(retjson,"partialtx",1); return(retjson); @@ -714,32 +697,17 @@ cJSON *addsignature(char *refcoin,char *acname,char *rawtx) return(0); } -char *get_gatewaysmultisig(char *refcoin,char *acname,char *txidaddr,int32_t *K) +bits256 gatewayspartialsign(int8_t type,char *refcoin,char *acname,bits256 txid,char *hex) { - char *retstr,*hexstr,*hex=0; cJSON *retjson; - if ( (retjson= get_komodocli("KMD",&retstr,acname,"gatewaysmultisig",txidaddr,"","","")) != 0 ) + char str[65],*retstr; cJSON *retjson; char function[64]; + if (type==0) sprintf(function,"%s","gatewayspartialsign"); + else if (type==1) sprintf(function,"%s","importgatewaypartialsign"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,txid),refcoin,hex,"")) != 0 ) { - if ((hexstr=jstr(retjson,"hex")) != 0 ) - { - if (strlen(hexstr)>0) hex = clonestr(hexstr); - } - *K=jint(retjson,"number_of_signs"); - free_json(retjson); - } - else if ( retstr != 0 ) - { - printf("error parsing gatewaysmultisig.(%s)\n",retstr); - free(retstr); - } - return(hex); -} - -bits256 gatewayspartialsign(char *refcoin,char *acname,bits256 txid,char *hex) -{ - char str[65],*retstr; cJSON *retjson; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewayspartialsign",bits256_str(str,txid),refcoin,hex,"")) != 0 ) - { - return(komodobroadcast(refcoin,acname,retjson)); + if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); + else printf("%s\n",jstr(retjson,"error")); + free(retjson); + return (txid); } else if ( retstr != 0 ) { @@ -749,42 +717,55 @@ bits256 gatewayspartialsign(char *refcoin,char *acname,bits256 txid,char *hex) return (zeroid); } -void gatewayscompletesigning(char *refcoin,char *acname,bits256 withtxid,char *coin,char *hex) +bits256 gatewayscompletesigning(int8_t type,char *refcoin,char *acname,bits256 withtxid,char *hex) { - char str[65],str2[65],*retstr; cJSON *retjson; - printf("spend %s %s/v2 as marker\n",acname,bits256_str(str,withtxid)); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewayscompletesigning",bits256_str(str,withtxid),coin,hex,"")) != 0 ) + char str[65],*retstr; cJSON *retjson; bits256 txid; char function[64]; + + if (type==0) sprintf(function,"%s","gatewayscompletesigning"); + else if (type==1) sprintf(function,"%s","importgatewaycompletesigning"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,withtxid),refcoin,hex,"")) != 0 ) { - komodobroadcast(refcoin,acname,retjson); - free_json(retjson); + if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); + else printf("%s\n",jstr(retjson,"error")); + free(retjson); + return (txid); } else if ( retstr != 0 ) { printf("error parsing gatewayscompletesigning.(%s)\n",retstr); free(retstr); } + return (zeroid); } -void gatewaysmarkdone(char *refcoin,char *acname,bits256 withtxid,char *coin) +bits256 gatewaysmarkdone(int8_t type,char *refcoin,char *acname,bits256 withtxid) { - char str[65],str2[65],*retstr; cJSON *retjson; - printf("spend %s %s/v2 as marker\n",acname,bits256_str(str,withtxid)); - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewaysmarkdone",bits256_str(str,withtxid),coin,"","")) != 0 ) + char str[65],str2[65],*retstr; cJSON *retjson; bits256 txid; ; char function[64]; + + if (type==0) sprintf(function,"%s","gatewaysmarkdone"); + else if (type==1) sprintf(function,"%s","importgatewaymarkdone"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bits256_str(str,withtxid),refcoin,"","")) != 0 ) { - komodobroadcast(refcoin,acname,retjson); - free_json(retjson); + if (strcmp("error",jstr(retjson,"result"))!=0) txid=broadcasttx(refcoin,acname,retjson); + else printf("%s\n",jstr(retjson,"error")); + free(retjson); + return (txid); } else if ( retstr != 0 ) { printf("error parsing gatewaysmarkdone.(%s)\n",retstr); free(retstr); } + return (zeroid); } -int32_t get_gatewaysinfo(char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr, char **pubkeys) +int32_t get_gatewaysinfo(int8_t type,char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr, char **pubkeys) { - char *oracle,*retstr,*name,*deposit,temp[128]; cJSON *retjson,*pubarray; int32_t n; - if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewaysinfo",bindtxidstr,"","","")) != 0 ) + char *oracle,*retstr,*name,*deposit,temp[128]; cJSON *retjson,*pubarray; int32_t n; char function[64]; + + if (type==0) sprintf(function,"%s","gatewaysinfo"); + else if (type==1) sprintf(function,"%s","importgatewayinfo"); + if ( (retjson= get_cli(refcoin,&retstr,acname,function,bindtxidstr,"","","")) != 0 ) { if ( (oracle= jstr(retjson,"oracletxid")) != 0 && strcmp(oracle,oraclestr) == 0 && (deposit= jstr(retjson,"deposit")) != 0 ) { @@ -809,7 +790,6 @@ int32_t get_gatewaysinfo(char *refcoin,char *acname,char *depositaddr,int32_t *M strcat(*pubkeys,temp); } } - else printf("%s != %s\n",oracle,oraclestr); free_json(retjson); } else if ( retstr != 0 ) @@ -882,57 +862,11 @@ int32_t markerexists(char *refcoin,char *acname,char *coinaddr) free_json(array); } } - fprintf(stderr,"Num=%d\n",num); return(num); } -int32_t markerfromthisnodeorunconfirmed(char *refcoin,char *acname,char *coinaddr) -{ - cJSON *array,*item,*rawtx,*vins,*vin; bits256 txid,tmptxid; int32_t i,n,m,num=0; char *retstr; - if ( (array= get_addressutxos(refcoin,acname,coinaddr)) != 0 ) - { - n=cJSON_GetArraySize(array); - for (i=0; i 0 ) - { - num = 1; - break; - } - } - } - free_json(array); - } else return(-1); - } - return(num); -} -void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t M,int32_t N) +void update_gatewayspending(int8_t type,char *refcoin,char *acname,char *bindtxidstr,int32_t M,int32_t N) { // check queue to prevent duplicate // check KMD chain and mempool for txidaddr @@ -941,9 +875,11 @@ void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t /// if enough sigs, sendrawtransaction and when it confirms spend marker (txid.2) /// if not enough sigs, post partially signed to acname with marker2 // monitor marker2, for the partially signed withdraws - cJSON *retjson,*pending,*item,*clijson; char str[65],*rawtx,*coinstr,*txidaddr,*signeraddr,*depositaddr,*withdrawaddr; int32_t i,j,n,K,retval,processed = 0; bits256 txid,cointxid,origtxid; int64_t satoshis; + cJSON *retjson,*pending,*item,*clijson; char str[65],str1[65],str2[65],*rawtx,*coinstr,*txidaddr,*signeraddr,*depositaddr,*withdrawaddr; + int32_t i,j,n,K,retval,processed = 0; bits256 txid,cointxid,withdrawtxid,lasttxid,completetxid; int64_t satoshis; + memset(&zeroid,0,sizeof(zeroid)); - if ( (retjson= get_gatewayspending("KMD",acname,bindtxidstr)) != 0 ) + if ( (retjson= get_gatewayspending(type,refcoin,acname,bindtxidstr)) != 0 ) { if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,refcoin) == 0 ) { @@ -954,86 +890,95 @@ void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t if ( processed != 0 ) // avoid out of utxo conditions break; item = jitem(pending,i); - origtxid = jbits256(item,"txid"); + withdrawtxid = jbits256(item,"withdrawtxid"); //process item.0 {"txid":"10ec8f4dad6903df6b249b361b879ac77b0617caad7629b97e10f29fa7e99a9b","txidaddr":"RMbite4TGugVmkGmu76ytPHDEQZQGSUjxz","withdrawaddr":"RNJmgYaFF5DbnrNUX6pMYz9rcnDKC2tuAc","amount":"1.00000000","depositaddr":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj","signeraddr":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj"} - if ( (txidaddr= jstr(item,"txidaddr")) != 0 && (withdrawaddr= jstr(item,"withdrawaddr")) != 0 && (depositaddr= jstr(item,"depositaddr")) != 0 && (signeraddr= jstr(item,"signeraddr")) != 0 ) + if ( (txidaddr=jstr(item,"withdrawtxidaddr"))!= 0 && (withdrawaddr=jstr(item,"withdrawaddr")) != 0 && (depositaddr= jstr(item,"depositaddr")) != 0 && (signeraddr= jstr(item,"signeraddr")) != 0 ) { - if ( (satoshis= jdouble(item,"amount")*SATOSHIDEN) != 0 && is_cJSON_True(jobj(item,"confirmed_or_notarized")) != 0 && markerfromthisnodeorunconfirmed("KMD",acname,txidaddr) == 0) + if ( (satoshis= jdouble(item,"amount")*SATOSHIDEN) != 0 && is_cJSON_True(jobj(item,"confirmed_or_notarized")) != 0) { if ( strcmp(depositaddr,signeraddr) == 0 ) { rawtx = createrawtx(refcoin,"",depositaddr,withdrawaddr,txidaddr,satoshis); if ( rawtx != 0 ) { - if ( (clijson= addsignature(refcoin,"",rawtx)) != 0 && is_cJSON_True(jobj(clijson,"complete")) != 0) + if ( (clijson=addsignature(refcoin,"",rawtx,M)) != 0 && is_cJSON_True(jobj(clijson,"complete")) != 0) { - gatewayscompletesigning("KMD",acname,origtxid,refcoin,jstr(clijson,"hex")); - fprintf(stderr,"withdraw %.8f %s to %s processed\n",(double)satoshis/SATOSHIDEN,refcoin,withdrawaddr); + txid=gatewayscompletesigning(type,refcoin,acname,withdrawtxid,jstr(clijson,"hex")); + if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s 1of1\n",bits256_str(str,withdrawtxid)); + else fprintf(stderr,"### SIGNING error broadcasting tx on %s",acname); free_json(clijson); - } - processed++; + processed++; + } free(rawtx); } else fprintf(stderr,"couldnt create rawtx\n"); } else { - if ( (rawtx= get_gatewaysmultisig(refcoin,acname,txidaddr,&K)) == 0) + lasttxid = jbits256(item,"last_txid"); + if ( lasttxid.txid==withdrawtxid.txid) { rawtx = createrawtx(refcoin,"",depositaddr,withdrawaddr,txidaddr,satoshis); } - if ( rawtx != 0 ) + else rawtx=jstr(item,"hex"); + K=jint(item,"number_of_signs"); + if ( rawtx!=0 && (clijson=addsignature(refcoin,"",rawtx,M)) != 0 ) { - if ( (clijson= addsignature(refcoin,"",rawtx)) != 0 ) - { - if ( is_cJSON_True(jobj(clijson,"complete")) != 0 ) - { - gatewayscompletesigning("KMD",acname,origtxid,refcoin,jstr(clijson,"hex")); - fprintf(stderr,"withdraw %.8f %s M.%d N.%d to %s processed\n",(double)satoshis/SATOSHIDEN,refcoin,M,N,withdrawaddr); - } - else if ( jint(clijson,"partialtx") != 0 ) - { - txid=gatewayspartialsign(refcoin,acname,origtxid,jstr(clijson,"hex")); - fprintf(stderr,"%d sign(s) %dof%d partialtx %s sent\n",K+1,M,N,bits256_str(str,txid)); - } - free_json(clijson); + if ( is_cJSON_True(jobj(clijson,"complete")) != 0 ) + { + txid=gatewayscompletesigning(type,refcoin,acname,lasttxid,jstr(clijson,"hex")); + if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %dof%d\n",bits256_str(str,withdrawtxid),K+1,N); + else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname); } + else if ( jint(clijson,"partialtx") != 0 ) + { + txid=gatewayspartialsign(type,refcoin,acname,lasttxid,jstr(clijson,"hex")); + if (txid.txid!=zeroid.txid) fprintf(stderr,"### SIGNING withdraw %s %d/%dof%d\n",bits256_str(str,withdrawtxid),K+1,M,N); + else fprintf(stderr,"### SIGNING error broadcasting tx on %s\n",acname); + } + free_json(clijson); processed++; - free(rawtx); - } else fprintf(stderr,"couldnt create msig rawtx\n"); - } - } + if ( lasttxid.txid==withdrawtxid.txid) free(rawtx); + } + } + } } } } } free_json(retjson); } - if ( (retjson= get_gatewaysprocessed("KMD",acname,bindtxidstr)) != 0 ) + if ( (retjson= get_gatewaysprocessed(type,refcoin,acname,bindtxidstr)) != 0 ) { if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,refcoin) == 0 ) { - if ( (pending=jarray(&n,retjson,"processed")) != 0 ) + if ((pending=jarray(&n,retjson,"processed")) != 0) { for (i=0; i %s\n",addr,destaddr); if ( (retjson= get_komodocli(coinstr,&retstr,acname,"z_mergetoaddress",addr,destaddr,"","")) != 0 ) { - /*{ - "remainingUTXOs": 0, - "remainingTransparentValue": 0.00000000, - "remainingNotes": 222, - "remainingShieldedValue": 5413.39093055, - "mergingUTXOs": 0, - "mergingTransparentValue": 0.00000000, - "mergingNotes": 10, - "mergingShieldedValue": 822.47447172, - "opid": "opid-f28f6261-4120-436c-aca5-859870a40a70" - }*/ - if ( (opstr= jstr(retjson,"opid")) != 0 ) - strcpy(opidstr,opstr); - retval = jint(retjson,"remainingNotes"); - fprintf(stderr,"%s\n",jprint(retjson,0)); - free_json(retjson); - } - else if ( retstr != 0 ) - { - fprintf(stderr,"z_mergetoaddress.(%s) -> opid.(%s)\n",coinstr,retstr); - strcpy(opidstr,retstr); - free(retstr); - } - return(retval); -} - -int32_t z_mergetoaddress(char *opidstr,char *coinstr,char *acname,char *destaddr) -{ - cJSON *retjson; char *retstr,addr[128],*opstr; int32_t retval = -1; - sprintf(addr,"[\\\"ANY_SPROUT\\\"]"); - //printf("z_sendmany from.(%s) -> %s\n",addr,destaddr); - if ( (retjson= get_komodocli(coinstr,&retstr,acname,"z_mergetoaddress",addr,destaddr,"","")) != 0 ) - { - /*{ - "remainingUTXOs": 0, - "remainingTransparentValue": 0.00000000, - "remainingNotes": 222, - "remainingShieldedValue": 5413.39093055, - "mergingUTXOs": 0, - "mergingTransparentValue": 0.00000000, - "mergingNotes": 10, - "mergingShieldedValue": 822.47447172, - "opid": "opid-f28f6261-4120-436c-aca5-859870a40a70" - }*/ if ( (opstr= jstr(retjson,"opid")) != 0 ) strcpy(opidstr,opstr); retval = jint(retjson,"remainingNotes"); diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp index 05dad9bf6..20f7a0567 100644 --- a/src/cc/dice.cpp +++ b/src/cc/dice.cpp @@ -264,12 +264,12 @@ int32_t dicefinish_utxosget(int32_t &total,struct dicefinish_utxo *utxos,int32_t int32_t n = 0; int64_t threshold = 2 * 10000; total = 0; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,false); { LOCK(mempool.cs); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ( myIsutxo_spentinmempool(it->first.txhash,(int32_t)it->first.index) == 0 ) + if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,it->first.txhash,(int32_t)it->first.index) == 0 ) { if ( it->second.satoshis < threshold || it->second.satoshis > 10*threshold ) continue; @@ -302,7 +302,7 @@ int32_t dice_betspent(char *debugstr,uint256 bettxid) } { //LOCK(mempool.cs); - if ( myIsutxo_spentinmempool(bettxid,0) != 0 || myIsutxo_spentinmempool(bettxid,1) != 0 ) + if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,bettxid,0) != 0 || myIsutxo_spentinmempool(ignoretxid,ignorevin,bettxid,1) != 0 ) { fprintf(stderr,"%s bettxid.%s already spent in mempool\n",debugstr,bettxid.GetHex().c_str()); return(-1); @@ -1051,10 +1051,12 @@ uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK char coinaddr[64],str[65]; uint64_t threshold,sbits,nValue,totalinputs = 0; uint256 txid,hash,proof,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; if ( maxinputs > 0 ) threshold = total / maxinputs; - else threshold = total / 64; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -1067,7 +1069,7 @@ uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK break; if ( j != mtx.vin.size() ) continue; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 ) { @@ -1106,7 +1108,7 @@ int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbit fundingPubKey = tx.vout[1].scriptPubKey; } else return(0); GetCCaddress(cp,coinaddr,dicepk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); entropyval = 0; int loops = 0; int numtxs = unspentOutputs.size()/2; @@ -1176,7 +1178,7 @@ int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbit continue; } } - if ( myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { entropytxid = txid; entropyval = tx.vout[0].nValue; @@ -1213,6 +1215,9 @@ int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbit } else { return(0); } + //fprintf(stderr,"numentropy tx %d: %.8f\n",n,(double)totalinputs/COIN); + entropytxs = n; + return(totalinputs); } bool DicePlanExists(CScript &fundingPubKey,uint256 &fundingtxid,struct CCcontract_info *cp,uint64_t refsbits,CPubKey dicepk,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks) @@ -1220,7 +1225,7 @@ bool DicePlanExists(CScript &fundingPubKey,uint256 &fundingtxid,struct CCcontrac char CCaddr[64]; uint64_t sbits=0; uint256 txid,hashBlock; CTransaction tx; std::vector > txids; GetCCaddress(cp,CCaddr,dicepk); - SetCCtxids(txids,cp->normaladdr); + SetCCtxids(txids,cp->normaladdr,false); if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan { //fprintf(stderr,"check fundingtxid\n"); @@ -1316,7 +1321,7 @@ UniValue DiceList() { UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits; int64_t minbet,maxbet,maxodds,timeoutblocks; char str[65]; cp = CCinit(&C,EVAL_DICE); - SetCCtxids(addressIndex,cp->normaladdr); + SetCCtxids(addressIndex,cp->normaladdr,false); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; @@ -1447,7 +1452,7 @@ std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet CCerror = "Your dealer is broke, find a new casino."; return(""); } - if ( myIsutxo_spentinmempool(entropytxid,0) != 0 ) + if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,entropytxid,0) != 0 ) { CCerror = "entropy txid is spent"; return(""); @@ -1657,7 +1662,7 @@ void *dealer0_loop(void *_arg) 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); + StartShutdown(); } fprintf(stderr,"dealer0 node running\n"); height = lastht = 0; @@ -1769,7 +1774,7 @@ double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettx return(0.); } std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/dilithium.c b/src/cc/dilithium.c new file mode 100644 index 000000000..5dfa1deda --- /dev/null +++ b/src/cc/dilithium.c @@ -0,0 +1,3682 @@ +/* Based on the public domain implementation in + * crypto_hash/keccakc512/simple/ from http://bench.cr.yp.to/supercop.html + * by Ronny Van Keer + * and the public domain "TweetFips202" implementation + * from https://twitter.com/tweetfips202 + * by Gilles Van Assche, Daniel J. Bernstein, and Peter Schwabe */ + +#include + +#define DBENCH_START() +#define DBENCH_STOP(arg) + +#include "dilithium.h" + + +#define NROUNDS 24 +#define ROL(a, offset) ((a << offset) ^ (a >> (64-offset))) + +/************************************************* +* Name: load64 +* +* Description: Load 8 bytes into uint64_t in little-endian order +* +* Arguments: - const uint8_t *x: pointer to input byte array +* +* Returns the loaded 64-bit unsigned integer +**************************************************/ +static uint64_t load64(const uint8_t *x) { + uint32_t i; + uint64_t r = 0; + + for (i = 0; i < 8; ++i) + r |= (uint64_t)x[i] << 8*i; + + return r; +} + +/************************************************* +* Name: store64 +* +* Description: Store a 64-bit integer to array of 8 bytes in little-endian order +* +* Arguments: - uint8_t *x: pointer to the output byte array (allocated) +* - uint64_t u: input 64-bit unsigned integer +**************************************************/ +static void store64(uint8_t *x, uint64_t u) { + uint32_t i; + + for(i = 0; i < 8; ++i) + x[i] = u >> 8*i; +} + +/* Keccak round constants */ +static const uint64_t KeccakF_RoundConstants[NROUNDS] = { + (uint64_t)0x0000000000000001ULL, + (uint64_t)0x0000000000008082ULL, + (uint64_t)0x800000000000808aULL, + (uint64_t)0x8000000080008000ULL, + (uint64_t)0x000000000000808bULL, + (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008081ULL, + (uint64_t)0x8000000000008009ULL, + (uint64_t)0x000000000000008aULL, + (uint64_t)0x0000000000000088ULL, + (uint64_t)0x0000000080008009ULL, + (uint64_t)0x000000008000000aULL, + (uint64_t)0x000000008000808bULL, + (uint64_t)0x800000000000008bULL, + (uint64_t)0x8000000000008089ULL, + (uint64_t)0x8000000000008003ULL, + (uint64_t)0x8000000000008002ULL, + (uint64_t)0x8000000000000080ULL, + (uint64_t)0x000000000000800aULL, + (uint64_t)0x800000008000000aULL, + (uint64_t)0x8000000080008081ULL, + (uint64_t)0x8000000000008080ULL, + (uint64_t)0x0000000080000001ULL, + (uint64_t)0x8000000080008008ULL +}; + +/************************************************* +* Name: KeccakF1600_StatePermute +* +* Description: The Keccak F1600 Permutation +* +* Arguments: - uint64_t *state: pointer to input/output Keccak state +**************************************************/ +static void KeccakF1600_StatePermute(uint64_t *state) +{ + int round; + + uint64_t Aba, Abe, Abi, Abo, Abu; + uint64_t Aga, Age, Agi, Ago, Agu; + uint64_t Aka, Ake, Aki, Ako, Aku; + uint64_t Ama, Ame, Ami, Amo, Amu; + uint64_t Asa, Ase, Asi, Aso, Asu; + uint64_t BCa, BCe, BCi, BCo, BCu; + uint64_t Da, De, Di, Do, Du; + uint64_t Eba, Ebe, Ebi, Ebo, Ebu; + uint64_t Ega, Ege, Egi, Ego, Egu; + uint64_t Eka, Eke, Eki, Eko, Eku; + uint64_t Ema, Eme, Emi, Emo, Emu; + uint64_t Esa, Ese, Esi, Eso, Esu; + + //copyFromState(A, state) + Aba = state[ 0]; + Abe = state[ 1]; + Abi = state[ 2]; + Abo = state[ 3]; + Abu = state[ 4]; + Aga = state[ 5]; + Age = state[ 6]; + Agi = state[ 7]; + Ago = state[ 8]; + Agu = state[ 9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for( round = 0; round < NROUNDS; round += 2 ) + { + // prepareTheta + BCa = Aba^Aga^Aka^Ama^Asa; + BCe = Abe^Age^Ake^Ame^Ase; + BCi = Abi^Agi^Aki^Ami^Asi; + BCo = Abo^Ago^Ako^Amo^Aso; + BCu = Abu^Agu^Aku^Amu^Asu; + + //thetaRhoPiChiIotaPrepareTheta(round , A, E) + Da = BCu^ROL(BCe, 1); + De = BCa^ROL(BCi, 1); + Di = BCe^ROL(BCo, 1); + Do = BCi^ROL(BCu, 1); + Du = BCo^ROL(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = ROL(Age, 44); + Aki ^= Di; + BCi = ROL(Aki, 43); + Amo ^= Do; + BCo = ROL(Amo, 21); + Asu ^= Du; + BCu = ROL(Asu, 14); + Eba = BCa ^((~BCe)& BCi ); + Eba ^= (uint64_t)KeccakF_RoundConstants[round]; + Ebe = BCe ^((~BCi)& BCo ); + Ebi = BCi ^((~BCo)& BCu ); + Ebo = BCo ^((~BCu)& BCa ); + Ebu = BCu ^((~BCa)& BCe ); + + Abo ^= Do; + BCa = ROL(Abo, 28); + Agu ^= Du; + BCe = ROL(Agu, 20); + Aka ^= Da; + BCi = ROL(Aka, 3); + Ame ^= De; + BCo = ROL(Ame, 45); + Asi ^= Di; + BCu = ROL(Asi, 61); + Ega = BCa ^((~BCe)& BCi ); + Ege = BCe ^((~BCi)& BCo ); + Egi = BCi ^((~BCo)& BCu ); + Ego = BCo ^((~BCu)& BCa ); + Egu = BCu ^((~BCa)& BCe ); + + Abe ^= De; + BCa = ROL(Abe, 1); + Agi ^= Di; + BCe = ROL(Agi, 6); + Ako ^= Do; + BCi = ROL(Ako, 25); + Amu ^= Du; + BCo = ROL(Amu, 8); + Asa ^= Da; + BCu = ROL(Asa, 18); + Eka = BCa ^((~BCe)& BCi ); + Eke = BCe ^((~BCi)& BCo ); + Eki = BCi ^((~BCo)& BCu ); + Eko = BCo ^((~BCu)& BCa ); + Eku = BCu ^((~BCa)& BCe ); + + Abu ^= Du; + BCa = ROL(Abu, 27); + Aga ^= Da; + BCe = ROL(Aga, 36); + Ake ^= De; + BCi = ROL(Ake, 10); + Ami ^= Di; + BCo = ROL(Ami, 15); + Aso ^= Do; + BCu = ROL(Aso, 56); + Ema = BCa ^((~BCe)& BCi ); + Eme = BCe ^((~BCi)& BCo ); + Emi = BCi ^((~BCo)& BCu ); + Emo = BCo ^((~BCu)& BCa ); + Emu = BCu ^((~BCa)& BCe ); + + Abi ^= Di; + BCa = ROL(Abi, 62); + Ago ^= Do; + BCe = ROL(Ago, 55); + Aku ^= Du; + BCi = ROL(Aku, 39); + Ama ^= Da; + BCo = ROL(Ama, 41); + Ase ^= De; + BCu = ROL(Ase, 2); + Esa = BCa ^((~BCe)& BCi ); + Ese = BCe ^((~BCi)& BCo ); + Esi = BCi ^((~BCo)& BCu ); + Eso = BCo ^((~BCu)& BCa ); + Esu = BCu ^((~BCa)& BCe ); + + // prepareTheta + BCa = Eba^Ega^Eka^Ema^Esa; + BCe = Ebe^Ege^Eke^Eme^Ese; + BCi = Ebi^Egi^Eki^Emi^Esi; + BCo = Ebo^Ego^Eko^Emo^Eso; + BCu = Ebu^Egu^Eku^Emu^Esu; + + //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) + Da = BCu^ROL(BCe, 1); + De = BCa^ROL(BCi, 1); + Di = BCe^ROL(BCo, 1); + Do = BCi^ROL(BCu, 1); + Du = BCo^ROL(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = ROL(Ege, 44); + Eki ^= Di; + BCi = ROL(Eki, 43); + Emo ^= Do; + BCo = ROL(Emo, 21); + Esu ^= Du; + BCu = ROL(Esu, 14); + Aba = BCa ^((~BCe)& BCi ); + Aba ^= (uint64_t)KeccakF_RoundConstants[round+1]; + Abe = BCe ^((~BCi)& BCo ); + Abi = BCi ^((~BCo)& BCu ); + Abo = BCo ^((~BCu)& BCa ); + Abu = BCu ^((~BCa)& BCe ); + + Ebo ^= Do; + BCa = ROL(Ebo, 28); + Egu ^= Du; + BCe = ROL(Egu, 20); + Eka ^= Da; + BCi = ROL(Eka, 3); + Eme ^= De; + BCo = ROL(Eme, 45); + Esi ^= Di; + BCu = ROL(Esi, 61); + Aga = BCa ^((~BCe)& BCi ); + Age = BCe ^((~BCi)& BCo ); + Agi = BCi ^((~BCo)& BCu ); + Ago = BCo ^((~BCu)& BCa ); + Agu = BCu ^((~BCa)& BCe ); + + Ebe ^= De; + BCa = ROL(Ebe, 1); + Egi ^= Di; + BCe = ROL(Egi, 6); + Eko ^= Do; + BCi = ROL(Eko, 25); + Emu ^= Du; + BCo = ROL(Emu, 8); + Esa ^= Da; + BCu = ROL(Esa, 18); + Aka = BCa ^((~BCe)& BCi ); + Ake = BCe ^((~BCi)& BCo ); + Aki = BCi ^((~BCo)& BCu ); + Ako = BCo ^((~BCu)& BCa ); + Aku = BCu ^((~BCa)& BCe ); + + Ebu ^= Du; + BCa = ROL(Ebu, 27); + Ega ^= Da; + BCe = ROL(Ega, 36); + Eke ^= De; + BCi = ROL(Eke, 10); + Emi ^= Di; + BCo = ROL(Emi, 15); + Eso ^= Do; + BCu = ROL(Eso, 56); + Ama = BCa ^((~BCe)& BCi ); + Ame = BCe ^((~BCi)& BCo ); + Ami = BCi ^((~BCo)& BCu ); + Amo = BCo ^((~BCu)& BCa ); + Amu = BCu ^((~BCa)& BCe ); + + Ebi ^= Di; + BCa = ROL(Ebi, 62); + Ego ^= Do; + BCe = ROL(Ego, 55); + Eku ^= Du; + BCi = ROL(Eku, 39); + Ema ^= Da; + BCo = ROL(Ema, 41); + Ese ^= De; + BCu = ROL(Ese, 2); + Asa = BCa ^((~BCe)& BCi ); + Ase = BCe ^((~BCi)& BCo ); + Asi = BCi ^((~BCo)& BCu ); + Aso = BCo ^((~BCu)& BCa ); + Asu = BCu ^((~BCa)& BCe ); + } + + //copyToState(state, A) + state[ 0] = Aba; + state[ 1] = Abe; + state[ 2] = Abi; + state[ 3] = Abo; + state[ 4] = Abu; + state[ 5] = Aga; + state[ 6] = Age; + state[ 7] = Agi; + state[ 8] = Ago; + state[ 9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; +} + +/************************************************* +* Name: keccak_absorb +* +* Description: Absorb step of Keccak; +* non-incremental, starts by zeroeing the state. +* +* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state +* - unsigned int r: rate in bytes (e.g., 168 for SHAKE128) +* - const uint8_t *m: pointer to input to be absorbed into s +* - int32_t mlen: length of input in bytes +* - uint8_t p: domain-separation byte for different +* Keccak-derived functions +**************************************************/ +static void keccak_absorb(uint64_t *s, + uint32_t r, + const uint8_t *m, + int32_t mlen, + uint8_t p) +{ + uint32_t i; + uint8_t t[200]; + DBENCH_START(); + + /* Zero state */ + for(i = 0; i < 25; ++i) + s[i] = 0; + + while(mlen >= r) { + for(i = 0; i < r/8; ++i) + s[i] ^= load64(m + 8*i); + + KeccakF1600_StatePermute(s); + mlen -= r; + m += r; + } + + for(i = 0; i < r; ++i) + t[i] = 0; + for(i = 0; i < mlen; ++i) + t[i] = m[i]; + t[i] = p; + t[r-1] |= 128; + for(i = 0; i < r/8; ++i) + s[i] ^= load64(t + 8*i); + + DBENCH_STOP(*tshake); +} + +/************************************************* +* Name: keccak_squeezeblocks +* +* Description: Squeeze step of Keccak. Squeezes full blocks of r bytes each. +* Modifies the state. Can be called multiple times to keep +* squeezing, i.e., is incremental. +* +* Arguments: - uint8_t *h: pointer to output blocks +* - int32_t int nblocks: number of blocks to be +* squeezed (written to h) +* - uint64_t *s: pointer to input/output Keccak state +* - uint32_t r: rate in bytes (e.g., 168 for SHAKE128) +**************************************************/ +static void keccak_squeezeblocks(uint8_t *h, + int32_t nblocks, + uint64_t *s, + uint32_t r) +{ + uint32_t i; + DBENCH_START(); + + while(nblocks > 0) { + KeccakF1600_StatePermute(s); + for(i=0; i < (r >> 3); i++) { + store64(h + 8*i, s[i]); + } + h += r; + --nblocks; + } + + DBENCH_STOP(*tshake); +} + +/************************************************* +* Name: shake128_absorb +* +* Description: Absorb step of the SHAKE128 XOF. +* non-incremental, starts by zeroeing the state. +* +* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state +* - const uint8_t *input: pointer to input to be absorbed +* into s +* - int32_t inlen: length of input in bytes +**************************************************/ +void shake128_absorb(uint64_t *s, + const uint8_t *input, + int32_t inlen) +{ + keccak_absorb(s, SHAKE128_RATE, input, inlen, 0x1F); +} + +/************************************************* +* Name: shake128_squeezeblocks +* +* Description: Squeeze step of SHAKE128 XOF. Squeezes full blocks of +* SHAKE128_RATE bytes each. Modifies the state. Can be called +* multiple times to keep squeezing, i.e., is incremental. +* +* Arguments: - uint8_t *output: pointer to output blocks +* - int32_t nblocks: number of blocks to be squeezed +* (written to output) +* - uint64_t *s: pointer to input/output Keccak state +**************************************************/ +void shake128_squeezeblocks(uint8_t *output, + int32_t nblocks, + uint64_t *s) +{ + keccak_squeezeblocks(output, nblocks, s, SHAKE128_RATE); +} + +/************************************************* +* Name: shake256_absorb +* +* Description: Absorb step of the SHAKE256 XOF. +* non-incremental, starts by zeroeing the state. +* +* Arguments: - uint64_t *s: pointer to (uninitialized) output Keccak state +* - const uint8_t *input: pointer to input to be absorbed +* into s +* - int32_t inlen: length of input in bytes +**************************************************/ +void shake256_absorb(uint64_t *s, + const uint8_t *input, + int32_t inlen) +{ + keccak_absorb(s, SHAKE256_RATE, input, inlen, 0x1F); +} + +/************************************************* +* Name: shake256_squeezeblocks +* +* Description: Squeeze step of SHAKE256 XOF. Squeezes full blocks of +* SHAKE256_RATE bytes each. Modifies the state. Can be called +* multiple times to keep squeezing, i.e., is incremental. +* +* Arguments: - uint8_t *output: pointer to output blocks +* - int32_t nblocks: number of blocks to be squeezed +* (written to output) +* - uint64_t *s: pointer to input/output Keccak state +**************************************************/ +void shake256_squeezeblocks(uint8_t *output, + int32_t nblocks, + uint64_t *s) +{ + keccak_squeezeblocks(output, nblocks, s, SHAKE256_RATE); +} + +/************************************************* +* Name: shake128 +* +* Description: SHAKE128 XOF with non-incremental API +* +* Arguments: - uint8_t *output: pointer to output +* - int32_t outlen: requested output length in bytes +* - const uint8_t *input: pointer to input +* - int32_t inlen: length of input in bytes +**************************************************/ +void shake128(uint8_t *output, + int32_t outlen, + const uint8_t *input, + int32_t inlen) +{ + uint32_t i,nblocks = outlen/SHAKE128_RATE; + uint8_t t[SHAKE128_RATE]; + uint64_t s[25]; + + shake128_absorb(s, input, inlen); + shake128_squeezeblocks(output, nblocks, s); + + output += nblocks*SHAKE128_RATE; + outlen -= nblocks*SHAKE128_RATE; + + if(outlen) { + shake128_squeezeblocks(t, 1, s); + for(i = 0; i < outlen; ++i) + output[i] = t[i]; + } +} + +/************************************************* +* Name: shake256 +* +* Description: SHAKE256 XOF with non-incremental API +* +* Arguments: - uint8_t *output: pointer to output +* - int32_t outlen: requested output length in bytes +* - const uint8_t *input: pointer to input +* - int32_t inlen: length of input in bytes +**************************************************/ +void shake256(uint8_t *output, + int32_t outlen, + const uint8_t *input, + int32_t inlen) +{ + uint32_t i,nblocks = outlen/SHAKE256_RATE; + uint8_t t[SHAKE256_RATE]; + uint64_t s[25]; + + shake256_absorb(s, input, inlen); + shake256_squeezeblocks(output, nblocks, s); + + output += nblocks*SHAKE256_RATE; + outlen -= nblocks*SHAKE256_RATE; + + if(outlen) { + shake256_squeezeblocks(t, 1, s); + for(i = 0; i < outlen; ++i) + output[i] = t[i]; + } +} +//#include "params.h" +//#include "reduce.h" +//#include "ntt.h" +//#include "poly.h" + +/* Roots of unity in order needed by forward ntt */ +static const uint32_t zetas[N] = {0, 25847, 5771523, 7861508, 237124, 7602457, 7504169, 466468, 1826347, 2353451, 8021166, 6288512, 3119733, 5495562, 3111497, 2680103, 2725464, 1024112, 7300517, 3585928, 7830929, 7260833, 2619752, 6271868, 6262231, 4520680, 6980856, 5102745, 1757237, 8360995, 4010497, 280005, 2706023, 95776, 3077325, 3530437, 6718724, 4788269, 5842901, 3915439, 4519302, 5336701, 3574422, 5512770, 3539968, 8079950, 2348700, 7841118, 6681150, 6736599, 3505694, 4558682, 3507263, 6239768, 6779997, 3699596, 811944, 531354, 954230, 3881043, 3900724, 5823537, 2071892, 5582638, 4450022, 6851714, 4702672, 5339162, 6927966, 3475950, 2176455, 6795196, 7122806, 1939314, 4296819, 7380215, 5190273, 5223087, 4747489, 126922, 3412210, 7396998, 2147896, 2715295, 5412772, 4686924, 7969390, 5903370, 7709315, 7151892, 8357436, 7072248, 7998430, 1349076, 1852771, 6949987, 5037034, 264944, 508951, 3097992, 44288, 7280319, 904516, 3958618, 4656075, 8371839, 1653064, 5130689, 2389356, 8169440, 759969, 7063561, 189548, 4827145, 3159746, 6529015, 5971092, 8202977, 1315589, 1341330, 1285669, 6795489, 7567685, 6940675, 5361315, 4499357, 4751448, 3839961, 2091667, 3407706, 2316500, 3817976, 5037939, 2244091, 5933984, 4817955, 266997, 2434439, 7144689, 3513181, 4860065, 4621053, 7183191, 5187039, 900702, 1859098, 909542, 819034, 495491, 6767243, 8337157, 7857917, 7725090, 5257975, 2031748, 3207046, 4823422, 7855319, 7611795, 4784579, 342297, 286988, 5942594, 4108315, 3437287, 5038140, 1735879, 203044, 2842341, 2691481, 5790267, 1265009, 4055324, 1247620, 2486353, 1595974, 4613401, 1250494, 2635921, 4832145, 5386378, 1869119, 1903435, 7329447, 7047359, 1237275, 5062207, 6950192, 7929317, 1312455, 3306115, 6417775, 7100756, 1917081, 5834105, 7005614, 1500165, 777191, 2235880, 3406031, 7838005, 5548557, 6709241, 6533464, 5796124, 4656147, 594136, 4603424, 6366809, 2432395, 2454455, 8215696, 1957272, 3369112, 185531, 7173032, 5196991, 162844, 1616392, 3014001, 810149, 1652634, 4686184, 6581310, 5341501, 3523897, 3866901, 269760, 2213111, 7404533, 1717735, 472078, 7953734, 1723600, 6577327, 1910376, 6712985, 7276084, 8119771, 4546524, 5441381, 6144432, 7959518, 6094090, 183443, 7403526, 1612842, 4834730, 7826001, 3919660, 8332111, 7018208, 3937738, 1400424, 7534263, 1976782}; + +/* Roots of unity in order needed by inverse ntt */ +static const uint32_t zetas_inv[N] = {6403635, 846154, 6979993, 4442679, 1362209, 48306, 4460757, 554416, 3545687, 6767575, 976891, 8196974, 2286327, 420899, 2235985, 2939036, 3833893, 260646, 1104333, 1667432, 6470041, 1803090, 6656817, 426683, 7908339, 6662682, 975884, 6167306, 8110657, 4513516, 4856520, 3038916, 1799107, 3694233, 6727783, 7570268, 5366416, 6764025, 8217573, 3183426, 1207385, 8194886, 5011305, 6423145, 164721, 5925962, 5948022, 2013608, 3776993, 7786281, 3724270, 2584293, 1846953, 1671176, 2831860, 542412, 4974386, 6144537, 7603226, 6880252, 1374803, 2546312, 6463336, 1279661, 1962642, 5074302, 7067962, 451100, 1430225, 3318210, 7143142, 1333058, 1050970, 6476982, 6511298, 2994039, 3548272, 5744496, 7129923, 3767016, 6784443, 5894064, 7132797, 4325093, 7115408, 2590150, 5688936, 5538076, 8177373, 6644538, 3342277, 4943130, 4272102, 2437823, 8093429, 8038120, 3595838, 768622, 525098, 3556995, 5173371, 6348669, 3122442, 655327, 522500, 43260, 1613174, 7884926, 7561383, 7470875, 6521319, 7479715, 3193378, 1197226, 3759364, 3520352, 4867236, 1235728, 5945978, 8113420, 3562462, 2446433, 6136326, 3342478, 4562441, 6063917, 4972711, 6288750, 4540456, 3628969, 3881060, 3019102, 1439742, 812732, 1584928, 7094748, 7039087, 7064828, 177440, 2409325, 1851402, 5220671, 3553272, 8190869, 1316856, 7620448, 210977, 5991061, 3249728, 6727353, 8578, 3724342, 4421799, 7475901, 1100098, 8336129, 5282425, 7871466, 8115473, 3343383, 1430430, 6527646, 7031341, 381987, 1308169, 22981, 1228525, 671102, 2477047, 411027, 3693493, 2967645, 5665122, 6232521, 983419, 4968207, 8253495, 3632928, 3157330, 3190144, 1000202, 4083598, 6441103, 1257611, 1585221, 6203962, 4904467, 1452451, 3041255, 3677745, 1528703, 3930395, 2797779, 6308525, 2556880, 4479693, 4499374, 7426187, 7849063, 7568473, 4680821, 1600420, 2140649, 4873154, 3821735, 4874723, 1643818, 1699267, 539299, 6031717, 300467, 4840449, 2867647, 4805995, 3043716, 3861115, 4464978, 2537516, 3592148, 1661693, 4849980, 5303092, 8284641, 5674394, 8100412, 4369920, 19422, 6623180, 3277672, 1399561, 3859737, 2118186, 2108549, 5760665, 1119584, 549488, 4794489, 1079900, 7356305, 5654953, 5700314, 5268920, 2884855, 5260684, 2091905, 359251, 6026966, 6554070, 7913949, 876248, 777960, 8143293, 518909, 2608894, 8354570}; + +/************************************************* +* Name: ntt +* +* Description: Forward NTT, in-place. No modular reduction is performed after +* additions or subtractions. Hence output coefficients can be up +* to 16*Q larger than the coefficients of the input polynomial. +* Output vector is in bitreversed order. +* +* Arguments: - uint32_t p[N]: input/output coefficient array +**************************************************/ +void ntt(uint32_t p[N]) { + uint32_t len, start, j, k; + uint32_t zeta, t; + + k = 1; + for(len = 128; len > 0; len >>= 1) { + for(start = 0; start < N; start = j + len) { + zeta = zetas[k++]; + for(j = start; j < start + len; ++j) { + t = montgomery_reduce((uint64_t)zeta * p[j + len]); + p[j + len] = p[j] + 2*Q - t; + p[j] = p[j] + t; + } + } + } +} + +/************************************************* +* Name: invntt_frominvmont +* +* Description: Inverse NTT and multiplication by Montgomery factor 2^32. +* In-place. No modular reductions after additions or +* subtractions. Input coefficient need to be smaller than 2*Q. +* Output coefficient are smaller than 2*Q. +* +* Arguments: - uint32_t p[N]: input/output coefficient array +**************************************************/ +void invntt_frominvmont(uint32_t p[N]) { + uint32_t start, len, j, k; + uint32_t t, zeta; + const uint32_t f = (((uint64_t)MONT*MONT % Q) * (Q-1) % Q) * ((Q-1) >> 8) % Q; + + k = 0; + for(len = 1; len < N; len <<= 1) { + for(start = 0; start < N; start = j + len) { + zeta = zetas_inv[k++]; + for(j = start; j < start + len; ++j) { + t = p[j]; + p[j] = t + p[j + len]; + p[j + len] = t + 256*Q - p[j + len]; + p[j + len] = montgomery_reduce((uint64_t)zeta * p[j + len]); + } + } + } + + for(j = 0; j < N; ++j) { + p[j] = montgomery_reduce((uint64_t)f * p[j]); + } +} +//#include "params.h" +//#include "poly.h" +//#include "polyvec.h" +//#include "packing.h" + +/************************************************* +* Name: pack_pk +* +* Description: Bit-pack public key pk = (rho, t1). +* +* Arguments: - uint8_t pk[]: output byte array +* - const uint8_t rho[]: byte array containing rho +* - const polyveck *t1: pointer to vector t1 +**************************************************/ +void pack_pk(uint8_t pk[CRYPTO_PUBLICKEYBYTES], + const uint8_t rho[SEEDBYTES], + const polyveck *t1) +{ + uint32_t i; + + for(i = 0; i < SEEDBYTES; ++i) + pk[i] = rho[i]; + pk += SEEDBYTES; + + for(i = 0; i < K; ++i) + polyt1_pack(pk + i*POLT1_SIZE_PACKED, t1->vec+i); +} + +/************************************************* +* Name: unpack_pk +* +* Description: Unpack public key pk = (rho, t1). +* +* Arguments: - const uint8_t rho[]: output byte array for rho +* - const polyveck *t1: pointer to output vector t1 +* - uint8_t pk[]: byte array containing bit-packed pk +**************************************************/ +void unpack_pk(uint8_t rho[SEEDBYTES], + polyveck *t1, + const uint8_t pk[CRYPTO_PUBLICKEYBYTES]) +{ + uint32_t i; + + for(i = 0; i < SEEDBYTES; ++i) + rho[i] = pk[i]; + pk += SEEDBYTES; + + for(i = 0; i < K; ++i) + polyt1_unpack(t1->vec+i, pk + i*POLT1_SIZE_PACKED); +} + +/************************************************* +* Name: pack_sk +* +* Description: Bit-pack secret key sk = (rho, key, tr, s1, s2, t0). +* +* Arguments: - uint8_t sk[]: output byte array +* - const uint8_t rho[]: byte array containing rho +* - const uint8_t key[]: byte array containing key +* - const uint8_t tr[]: byte array containing tr +* - const polyvecl *s1: pointer to vector s1 +* - const polyveck *s2: pointer to vector s2 +* - const polyveck *t0: pointer to vector t0 +**************************************************/ +void pack_sk(uint8_t sk[CRYPTO_SECRETKEYBYTES], + const uint8_t rho[SEEDBYTES], + const uint8_t key[SEEDBYTES], + const uint8_t tr[CRHBYTES], + const polyvecl *s1, + const polyveck *s2, + const polyveck *t0) +{ + uint32_t i; + + for(i = 0; i < SEEDBYTES; ++i) + sk[i] = rho[i]; + sk += SEEDBYTES; + + for(i = 0; i < SEEDBYTES; ++i) + sk[i] = key[i]; + sk += SEEDBYTES; + + for(i = 0; i < CRHBYTES; ++i) + sk[i] = tr[i]; + sk += CRHBYTES; + + for(i = 0; i < L; ++i) + polyeta_pack(sk + i*POLETA_SIZE_PACKED, s1->vec+i); + sk += L*POLETA_SIZE_PACKED; + + for(i = 0; i < K; ++i) + polyeta_pack(sk + i*POLETA_SIZE_PACKED, s2->vec+i); + sk += K*POLETA_SIZE_PACKED; + + for(i = 0; i < K; ++i) + polyt0_pack(sk + i*POLT0_SIZE_PACKED, t0->vec+i); +} + +/************************************************* +* Name: unpack_sk +* +* Description: Unpack secret key sk = (rho, key, tr, s1, s2, t0). +* +* Arguments: - const uint8_t rho[]: output byte array for rho +* - const uint8_t key[]: output byte array for key +* - const uint8_t tr[]: output byte array for tr +* - const polyvecl *s1: pointer to output vector s1 +* - const polyveck *s2: pointer to output vector s2 +* - const polyveck *r0: pointer to output vector t0 +* - uint8_t sk[]: byte array containing bit-packed sk +**************************************************/ +void unpack_sk(uint8_t rho[SEEDBYTES], + uint8_t key[SEEDBYTES], + uint8_t tr[CRHBYTES], + polyvecl *s1, + polyveck *s2, + polyveck *t0, + const uint8_t sk[CRYPTO_SECRETKEYBYTES]) +{ + uint32_t i; + + for(i = 0; i < SEEDBYTES; ++i) + rho[i] = sk[i]; + sk += SEEDBYTES; + + for(i = 0; i < SEEDBYTES; ++i) + key[i] = sk[i]; + sk += SEEDBYTES; + + for(i = 0; i < CRHBYTES; ++i) + tr[i] = sk[i]; + sk += CRHBYTES; + + for(i=0; i < L; ++i) + polyeta_unpack(s1->vec+i, sk + i*POLETA_SIZE_PACKED); + sk += L*POLETA_SIZE_PACKED; + + for(i=0; i < K; ++i) + polyeta_unpack(s2->vec+i, sk + i*POLETA_SIZE_PACKED); + sk += K*POLETA_SIZE_PACKED; + + for(i=0; i < K; ++i) + polyt0_unpack(t0->vec+i, sk + i*POLT0_SIZE_PACKED); +} + +/************************************************* +* Name: pack_sig +* +* Description: Bit-pack signature sig = (z, h, c). +* +* Arguments: - uint8_t sig[]: output byte array +* - const polyvecl *z: pointer to vector z +* - const polyveck *h: pointer to hint vector h +* - const poly *c: pointer to challenge polynomial +**************************************************/ +void pack_sig(uint8_t sig[CRYPTO_BYTES], + const polyvecl *z, + const polyveck *h, + const poly *c) +{ + uint32_t i, j, k; + uint64_t signs, mask; + + for(i = 0; i < L; ++i) + polyz_pack(sig + i*POLZ_SIZE_PACKED, z->vec+i); + sig += L*POLZ_SIZE_PACKED; + + /* Encode h */ + k = 0; + for(i = 0; i < K; ++i) { + for(j = 0; j < N; ++j) + if(h->vec[i].coeffs[j] != 0) + sig[k++] = j; + + sig[OMEGA + i] = k; + } + while(k < OMEGA) sig[k++] = 0; + sig += OMEGA + K; + + /* Encode c */ + signs = 0; + mask = 1; + for(i = 0; i < N/8; ++i) { + sig[i] = 0; + for(j = 0; j < 8; ++j) { + if(c->coeffs[8*i+j] != 0) { + sig[i] |= (1U << j); + if(c->coeffs[8*i+j] == (Q - 1)) signs |= mask; + mask <<= 1; + } + } + } + sig += N/8; + for(i = 0; i < 8; ++i) + sig[i] = signs >> 8*i; +} + +/************************************************* +* Name: unpack_sig +* +* Description: Unpack signature sig = (z, h, c). +* +* Arguments: - polyvecl *z: pointer to output vector z +* - polyveck *h: pointer to output hint vector h +* - poly *c: pointer to output challenge polynomial +* - const uint8_t sig[]: byte array containing +* bit-packed signature +* +* Returns 1 in case of malformed signature; otherwise 0. +**************************************************/ +int unpack_sig(polyvecl *z, + polyveck *h, + poly *c, + const uint8_t sig[CRYPTO_BYTES]) +{ + uint32_t i, j, k; + uint64_t signs, mask; + + for(i = 0; i < L; ++i) + polyz_unpack(z->vec+i, sig + i*POLZ_SIZE_PACKED); + sig += L*POLZ_SIZE_PACKED; + + /* Decode h */ + k = 0; + for(i = 0; i < K; ++i) { + for(j = 0; j < N; ++j) + h->vec[i].coeffs[j] = 0; + + if(sig[OMEGA + i] < k || sig[OMEGA + i] > OMEGA) + return 1; + + for(j = k; j < sig[OMEGA + i]; ++j) { + /* Coefficients are ordered for strong unforgeability */ + if(j > k && sig[j] <= sig[j-1]) return 1; + h->vec[i].coeffs[sig[j]] = 1; + } + + k = sig[OMEGA + i]; + } + + /* Extra indices are zero for strong unforgeability */ + for(j = k; j < OMEGA; ++j) + if(sig[j]) + return 1; + + sig += OMEGA + K; + + /* Decode c */ + for(i = 0; i < N; ++i) + c->coeffs[i] = 0; + + signs = 0; + for(i = 0; i < 8; ++i) + signs |= (uint64_t)sig[N/8+i] << 8*i; + + /* Extra sign bits are zero for strong unforgeability */ + if(signs >> 60) + return 1; + + mask = 1; + for(i = 0; i < N/8; ++i) { + for(j = 0; j < 8; ++j) { + if((sig[i] >> j) & 0x01) { + c->coeffs[8*i+j] = (signs & mask) ? Q - 1 : 1; + mask <<= 1; + } + } + } + + return 0; +} +//#include +//#include "test/cpucycles.h" +//#include "fips202.h" +//#include "params.h" +//#include "reduce.h" +//#include "rounding.h" +//#include "ntt.h" +//#include "poly.h" + +#ifdef DBENCH +extern const uint64_t timing_overhead; +extern uint64_t *tred, *tadd, *tmul, *tround, *tsample, *tpack; +#endif + +/************************************************* +* Name: poly_reduce +* +* Description: Reduce all coefficients of input polynomial to representative +* in [0,2*Q[. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void poly_reduce(poly *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a->coeffs[i] = reduce32(a->coeffs[i]); + + DBENCH_STOP(*tred); +} + +/************************************************* +* Name: poly_csubq +* +* Description: For all coefficients of input polynomial subtract Q if +* coefficient is bigger than Q. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void poly_csubq(poly *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a->coeffs[i] = csubq(a->coeffs[i]); + + DBENCH_STOP(*tred); +} + +/************************************************* +* Name: poly_freeze +* +* Description: Reduce all coefficients of the polynomial to standard +* representatives. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void poly_freeze(poly *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a->coeffs[i] = freeze(a->coeffs[i]); + + DBENCH_STOP(*tred); +} + +/************************************************* +* Name: poly_add +* +* Description: Add polynomials. No modular reduction is performed. +* +* Arguments: - poly *c: pointer to output polynomial +* - const poly *a: pointer to first summand +* - const poly *b: pointer to second summand +**************************************************/ +void poly_add(poly *c, const poly *a, const poly *b) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + c->coeffs[i] = a->coeffs[i] + b->coeffs[i]; + + DBENCH_STOP(*tadd); +} + +/************************************************* +* Name: poly_sub +* +* Description: Subtract polynomials. Assumes coefficients of second input +* polynomial to be less than 2*Q. No modular reduction is +* performed. +* +* Arguments: - poly *c: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial to be +* subtraced from first input polynomial +**************************************************/ +void poly_sub(poly *c, const poly *a, const poly *b) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + c->coeffs[i] = a->coeffs[i] + 2*Q - b->coeffs[i]; + + DBENCH_STOP(*tadd); +} + +/************************************************* +* Name: poly_neg +* +* Description: Negate polynomial. Assumes input coefficients to be standard +* representatives. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void poly_neg(poly *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a->coeffs[i] = Q - a->coeffs[i]; + + DBENCH_STOP(*tadd); +} + +/************************************************* +* Name: poly_shiftl +* +* Description: Multiply polynomial by 2^k without modular reduction. Assumes +* input coefficients to be less than 2^{32-k}. +* +* Arguments: - poly *a: pointer to input/output polynomial +* - uint32_t k: exponent +**************************************************/ +void poly_shiftl(poly *a, uint32_t k) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a->coeffs[i] <<= k; + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: poly_ntt +* +* Description: Forward NTT. Output coefficients can be up to 16*Q larger than +* input coefficients. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void poly_ntt(poly *a) { + DBENCH_START(); + + ntt(a->coeffs); + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: poly_invntt_montgomery +* +* Description: Inverse NTT and multiplication with 2^{32}. Input coefficients +* need to be less than 2*Q. Output coefficients are less than 2*Q. +* +* Arguments: - poly *a: pointer to input/output polynomial +**************************************************/ +void poly_invntt_montgomery(poly *a) { + DBENCH_START(); + + invntt_frominvmont(a->coeffs); + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: poly_pointwise_invmontgomery +* +* Description: Pointwise multiplication of polynomials in NTT domain +* representation and multiplication of resulting polynomial +* with 2^{-32}. Output coefficients are less than 2*Q if input +* coefficient are less than 22*Q. +* +* Arguments: - poly *c: pointer to output polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +**************************************************/ +void poly_pointwise_invmontgomery(poly *c, const poly *a, const poly *b) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + c->coeffs[i] = montgomery_reduce((uint64_t)a->coeffs[i] * b->coeffs[i]); + + DBENCH_STOP(*tmul); +} + +/************************************************* +* Name: poly_power2round +* +* Description: For all coefficients c of the input polynomial, +* compute c0, c1 such that c mod Q = c1*2^D + c0 +* with -2^{D-1} < c0 <= 2^{D-1}. Assumes coefficients to be +* standard representatives. +* +* Arguments: - poly *a1: pointer to output polynomial with coefficients c1 +* - poly *a0: pointer to output polynomial with coefficients Q + a0 +* - const poly *v: pointer to input polynomial +**************************************************/ +void poly_power2round(poly *a1, poly *a0, const poly *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a1->coeffs[i] = power2round(a->coeffs[i], a0->coeffs+i); + + DBENCH_STOP(*tround); +} + +/************************************************* +* Name: poly_decompose +* +* Description: For all coefficients c of the input polynomial, +* compute high and low bits c0, c1 such c mod Q = c1*ALPHA + c0 +* with -ALPHA/2 < c0 <= ALPHA/2 except c1 = (Q-1)/ALPHA where we +* set c1 = 0 and -ALPHA/2 <= c0 = c mod Q - Q < 0. +* Assumes coefficients to be standard representatives. +* +* Arguments: - poly *a1: pointer to output polynomial with coefficients c1 +* - poly *a0: pointer to output polynomial with coefficients Q + a0 +* - const poly *c: pointer to input polynomial +**************************************************/ +void poly_decompose(poly *a1, poly *a0, const poly *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a1->coeffs[i] = decompose(a->coeffs[i], a0->coeffs+i); + + DBENCH_STOP(*tround); +} + +/************************************************* +* Name: poly_make_hint +* +* Description: Compute hint polynomial. The coefficients of which indicate +* whether the high bits of the corresponding coefficients +* of the first input polynomial and of the sum of the input +* polynomials differ. +* +* Arguments: - poly *h: pointer to output hint polynomial +* - const poly *a: pointer to first input polynomial +* - const poly *b: pointer to second input polynomial +* +* Returns number of 1 bits. +**************************************************/ +uint32_t poly_make_hint(poly *h, const poly *a, const poly *b) { + uint32_t i, s = 0; + DBENCH_START(); + + for(i = 0; i < N; ++i) { + h->coeffs[i] = make_hint(a->coeffs[i], b->coeffs[i]); + s += h->coeffs[i]; + } + + DBENCH_STOP(*tround); + return s; +} + +/************************************************* +* Name: poly_use_hint +* +* Description: Use hint polynomial to correct the high bits of a polynomial. +* +* Arguments: - poly *a: pointer to output polynomial with corrected high bits +* - const poly *b: pointer to input polynomial +* - const poly *h: pointer to input hint polynomial +**************************************************/ +void poly_use_hint(poly *a, const poly *b, const poly *h) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N; ++i) + a->coeffs[i] = use_hint(b->coeffs[i], h->coeffs[i]); + + DBENCH_STOP(*tround); +} + +/************************************************* +* Name: poly_chknorm +* +* Description: Check infinity norm of polynomial against given bound. +* Assumes input coefficients to be standard representatives. +* +* Arguments: - const poly *a: pointer to polynomial +* - uint32_t B: norm bound +* +* Returns 0 if norm is strictly smaller than B and 1 otherwise. +**************************************************/ +int poly_chknorm(const poly *a, uint32_t B) { + uint32_t i; + int32_t t; + DBENCH_START(); + + /* It is ok to leak which coefficient violates the bound since + the probability for each coefficient is independent of secret + data but we must not leak the sign of the centralized representative. */ + for(i = 0; i < N; ++i) { + /* Absolute value of centralized representative */ + t = (Q-1)/2 - a->coeffs[i]; + t ^= (t >> 31); + t = (Q-1)/2 - t; + + if((uint32_t)t >= B) { + DBENCH_STOP(*tsample); + return 1; + } + } + + DBENCH_STOP(*tsample); + return 0; +} + +/************************************************* +* Name: poly_uniform +* +* Description: Sample uniformly random polynomial using stream of random bytes. +* Assumes that enough random bytes are given (e.g. +* 5*SHAKE128_RATE bytes). +* +* Arguments: - poly *a: pointer to output polynomial +* - const uint8_t *buf: array of random bytes +**************************************************/ +void poly_uniform(poly *a, const uint8_t *buf) { + uint32_t ctr, pos; + uint32_t t; + DBENCH_START(); + + ctr = pos = 0; + while(ctr < N) { + t = buf[pos++]; + t |= (uint32_t)buf[pos++] << 8; + t |= (uint32_t)buf[pos++] << 16; + t &= 0x7FFFFF; + + if(t < Q) + a->coeffs[ctr++] = t; + } + + DBENCH_STOP(*tsample); +} + +/************************************************* +* Name: rej_eta +* +* Description: Sample uniformly random coefficients in [-ETA, ETA] by +* performing rejection sampling using array of random bytes. +* +* Arguments: - uint32_t *a: pointer to output array (allocated) +* - uint32_t len: number of coefficients to be sampled +* - const uint8_t *buf: array of random bytes +* - uint32_t buflen: length of array of random bytes +* +* Returns number of sampled coefficients. Can be smaller than len if not enough +* random bytes were given. +**************************************************/ +static uint32_t rej_eta(uint32_t *a, + uint32_t len, + const uint8_t *buf, + uint32_t buflen) +{ +#if ETA > 7 +#error "rej_eta() assumes ETA <= 7" +#endif + uint32_t ctr, pos; + uint8_t t0, t1; + DBENCH_START(); + + ctr = pos = 0; + while(ctr < len && pos < buflen) { +#if ETA <= 3 + t0 = buf[pos] & 0x07; + t1 = buf[pos++] >> 5; +#else + t0 = buf[pos] & 0x0F; + t1 = buf[pos++] >> 4; +#endif + + if(t0 <= 2*ETA) + a[ctr++] = Q + ETA - t0; + if(t1 <= 2*ETA && ctr < len) + a[ctr++] = Q + ETA - t1; + } + + DBENCH_STOP(*tsample); + return ctr; +} + +/************************************************* +* Name: poly_uniform_eta +* +* Description: Sample polynomial with uniformly random coefficients +* in [-ETA,ETA] by performing rejection sampling using the +* output stream from SHAKE256(seed|nonce). +* +* Arguments: - poly *a: pointer to output polynomial +* - const uint8_t seed[]: byte array with seed of length +* SEEDBYTES +* - uint8_t nonce: nonce byte +**************************************************/ +void poly_uniform_eta(poly *a, + const uint8_t seed[SEEDBYTES], + uint8_t nonce) +{ + uint32_t i, ctr; + uint8_t inbuf[SEEDBYTES + 1]; + /* Probability that we need more than 2 blocks: < 2^{-84} + Probability that we need more than 3 blocks: < 2^{-352} */ + uint8_t outbuf[2*SHAKE256_RATE]; + uint64_t state[25]; + + for(i= 0; i < SEEDBYTES; ++i) + inbuf[i] = seed[i]; + inbuf[SEEDBYTES] = nonce; + + shake256_absorb(state, inbuf, SEEDBYTES + 1); + shake256_squeezeblocks(outbuf, 2, state); + + ctr = rej_eta(a->coeffs, N, outbuf, 2*SHAKE256_RATE); + if(ctr < N) { + shake256_squeezeblocks(outbuf, 1, state); + rej_eta(a->coeffs + ctr, N - ctr, outbuf, SHAKE256_RATE); + } +} + +/************************************************* +* Name: rej_gamma1m1 +* +* Description: Sample uniformly random coefficients +* in [-(GAMMA1 - 1), GAMMA1 - 1] by performing rejection sampling +* using array of random bytes. +* +* Arguments: - uint32_t *a: pointer to output array (allocated) +* - uint32_t len: number of coefficients to be sampled +* - const uint8_t *buf: array of random bytes +* - uint32_t buflen: length of array of random bytes +* +* Returns number of sampled coefficients. Can be smaller than len if not enough +* random bytes were given. +**************************************************/ +static uint32_t rej_gamma1m1(uint32_t *a, + uint32_t len, + const uint8_t *buf, + uint32_t buflen) +{ +#if GAMMA1 > (1 << 19) +#error "rej_gamma1m1() assumes GAMMA1 - 1 fits in 19 bits" +#endif + uint32_t ctr, pos; + uint32_t t0, t1; + DBENCH_START(); + + ctr = pos = 0; + while(ctr < len && pos + 5 <= buflen) { + t0 = buf[pos]; + t0 |= (uint32_t)buf[pos + 1] << 8; + t0 |= (uint32_t)buf[pos + 2] << 16; + t0 &= 0xFFFFF; + + t1 = buf[pos + 2] >> 4; + t1 |= (uint32_t)buf[pos + 3] << 4; + t1 |= (uint32_t)buf[pos + 4] << 12; + + pos += 5; + + if(t0 <= 2*GAMMA1 - 2) + a[ctr++] = Q + GAMMA1 - 1 - t0; + if(t1 <= 2*GAMMA1 - 2 && ctr < len) + a[ctr++] = Q + GAMMA1 - 1 - t1; + } + + DBENCH_STOP(*tsample); + return ctr; +} + +/************************************************* +* Name: poly_uniform_gamma1m1 +* +* Description: Sample polynomial with uniformly random coefficients +* in [-(GAMMA1 - 1), GAMMA1 - 1] by performing rejection +* sampling on output stream of SHAKE256(seed|nonce). +* +* Arguments: - poly *a: pointer to output polynomial +* - const uint8_t seed[]: byte array with seed of length +* SEEDBYTES + CRHBYTES +* - uint16_t nonce: 16-bit nonce +**************************************************/ +void poly_uniform_gamma1m1(poly *a, + const uint8_t seed[SEEDBYTES + CRHBYTES], + uint16_t nonce) +{ + uint32_t i, ctr; + uint8_t inbuf[SEEDBYTES + CRHBYTES + 2]; + /* Probability that we need more than 5 blocks: < 2^{-81} + Probability that we need more than 6 blocks: < 2^{-467} */ + uint8_t outbuf[5*SHAKE256_RATE]; + uint64_t state[25]; + + for(i = 0; i < SEEDBYTES + CRHBYTES; ++i) + inbuf[i] = seed[i]; + inbuf[SEEDBYTES + CRHBYTES] = nonce & 0xFF; + inbuf[SEEDBYTES + CRHBYTES + 1] = nonce >> 8; + + shake256_absorb(state, inbuf, SEEDBYTES + CRHBYTES + 2); + shake256_squeezeblocks(outbuf, 5, state); + + ctr = rej_gamma1m1(a->coeffs, N, outbuf, 5*SHAKE256_RATE); + if(ctr < N) { + /* There are no bytes left in outbuf + since 5*SHAKE256_RATE is divisible by 5 */ + shake256_squeezeblocks(outbuf, 1, state); + rej_gamma1m1(a->coeffs + ctr, N - ctr, outbuf, SHAKE256_RATE); + } +} + +/************************************************* +* Name: polyeta_pack +* +* Description: Bit-pack polynomial with coefficients in [-ETA,ETA]. +* Input coefficients are assumed to lie in [Q-ETA,Q+ETA]. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLETA_SIZE_PACKED bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void polyeta_pack(uint8_t *r, const poly *a) { +#if ETA > 7 +#error "polyeta_pack() assumes ETA <= 7" +#endif + uint32_t i; + uint8_t t[8]; + DBENCH_START(); + +#if ETA <= 3 + for(i = 0; i < N/8; ++i) { + t[0] = Q + ETA - a->coeffs[8*i+0]; + t[1] = Q + ETA - a->coeffs[8*i+1]; + t[2] = Q + ETA - a->coeffs[8*i+2]; + t[3] = Q + ETA - a->coeffs[8*i+3]; + t[4] = Q + ETA - a->coeffs[8*i+4]; + t[5] = Q + ETA - a->coeffs[8*i+5]; + t[6] = Q + ETA - a->coeffs[8*i+6]; + t[7] = Q + ETA - a->coeffs[8*i+7]; + + r[3*i+0] = t[0]; + r[3*i+0] |= t[1] << 3; + r[3*i+0] |= t[2] << 6; + r[3*i+1] = t[2] >> 2; + r[3*i+1] |= t[3] << 1; + r[3*i+1] |= t[4] << 4; + r[3*i+1] |= t[5] << 7; + r[3*i+2] = t[5] >> 1; + r[3*i+2] |= t[6] << 2; + r[3*i+2] |= t[7] << 5; + } +#else + for(i = 0; i < N/2; ++i) { + t[0] = Q + ETA - a->coeffs[2*i+0]; + t[1] = Q + ETA - a->coeffs[2*i+1]; + r[i] = t[0] | (t[1] << 4); + } +#endif + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyeta_unpack +* +* Description: Unpack polynomial with coefficients in [-ETA,ETA]. +* Output coefficients lie in [Q-ETA,Q+ETA]. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void polyeta_unpack(poly *r, const uint8_t *a) { + uint32_t i; + DBENCH_START(); + +#if ETA <= 3 + for(i = 0; i < N/8; ++i) { + r->coeffs[8*i+0] = a[3*i+0] & 0x07; + r->coeffs[8*i+1] = (a[3*i+0] >> 3) & 0x07; + r->coeffs[8*i+2] = (a[3*i+0] >> 6) | ((a[3*i+1] & 0x01) << 2); + r->coeffs[8*i+3] = (a[3*i+1] >> 1) & 0x07; + r->coeffs[8*i+4] = (a[3*i+1] >> 4) & 0x07; + r->coeffs[8*i+5] = (a[3*i+1] >> 7) | ((a[3*i+2] & 0x03) << 1); + r->coeffs[8*i+6] = (a[3*i+2] >> 2) & 0x07; + r->coeffs[8*i+7] = (a[3*i+2] >> 5); + + r->coeffs[8*i+0] = Q + ETA - r->coeffs[8*i+0]; + r->coeffs[8*i+1] = Q + ETA - r->coeffs[8*i+1]; + r->coeffs[8*i+2] = Q + ETA - r->coeffs[8*i+2]; + r->coeffs[8*i+3] = Q + ETA - r->coeffs[8*i+3]; + r->coeffs[8*i+4] = Q + ETA - r->coeffs[8*i+4]; + r->coeffs[8*i+5] = Q + ETA - r->coeffs[8*i+5]; + r->coeffs[8*i+6] = Q + ETA - r->coeffs[8*i+6]; + r->coeffs[8*i+7] = Q + ETA - r->coeffs[8*i+7]; + } +#else + for(i = 0; i < N/2; ++i) { + r->coeffs[2*i+0] = a[i] & 0x0F; + r->coeffs[2*i+1] = a[i] >> 4; + r->coeffs[2*i+0] = Q + ETA - r->coeffs[2*i+0]; + r->coeffs[2*i+1] = Q + ETA - r->coeffs[2*i+1]; + } +#endif + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyt1_pack +* +* Description: Bit-pack polynomial t1 with coefficients fitting in 9 bits. +* Input coefficients are assumed to be standard representatives. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLT1_SIZE_PACKED bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void polyt1_pack(uint8_t *r, const poly *a) { +#if D != 14 +#error "polyt1_pack() assumes D == 14" +#endif + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N/8; ++i) { + r[9*i+0] = a->coeffs[8*i+0] & 0xFF; + r[9*i+1] = (a->coeffs[8*i+0] >> 8) | ((a->coeffs[8*i+1] & 0x7F) << 1); + r[9*i+2] = (a->coeffs[8*i+1] >> 7) | ((a->coeffs[8*i+2] & 0x3F) << 2); + r[9*i+3] = (a->coeffs[8*i+2] >> 6) | ((a->coeffs[8*i+3] & 0x1F) << 3); + r[9*i+4] = (a->coeffs[8*i+3] >> 5) | ((a->coeffs[8*i+4] & 0x0F) << 4); + r[9*i+5] = (a->coeffs[8*i+4] >> 4) | ((a->coeffs[8*i+5] & 0x07) << 5); + r[9*i+6] = (a->coeffs[8*i+5] >> 3) | ((a->coeffs[8*i+6] & 0x03) << 6); + r[9*i+7] = (a->coeffs[8*i+6] >> 2) | ((a->coeffs[8*i+7] & 0x01) << 7); + r[9*i+8] = a->coeffs[8*i+7] >> 1; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyt1_unpack +* +* Description: Unpack polynomial t1 with 9-bit coefficients. +* Output coefficients are standard representatives. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void polyt1_unpack(poly *r, const uint8_t *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N/8; ++i) { + r->coeffs[8*i+0] = a[9*i+0] | ((uint32_t)(a[9*i+1] & 0x01) << 8); + r->coeffs[8*i+1] = (a[9*i+1] >> 1) | ((uint32_t)(a[9*i+2] & 0x03) << 7); + r->coeffs[8*i+2] = (a[9*i+2] >> 2) | ((uint32_t)(a[9*i+3] & 0x07) << 6); + r->coeffs[8*i+3] = (a[9*i+3] >> 3) | ((uint32_t)(a[9*i+4] & 0x0F) << 5); + r->coeffs[8*i+4] = (a[9*i+4] >> 4) | ((uint32_t)(a[9*i+5] & 0x1F) << 4); + r->coeffs[8*i+5] = (a[9*i+5] >> 5) | ((uint32_t)(a[9*i+6] & 0x3F) << 3); + r->coeffs[8*i+6] = (a[9*i+6] >> 6) | ((uint32_t)(a[9*i+7] & 0x7F) << 2); + r->coeffs[8*i+7] = (a[9*i+7] >> 7) | ((uint32_t)(a[9*i+8] & 0xFF) << 1); + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyt0_pack +* +* Description: Bit-pack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}]. +* Input coefficients are assumed to lie in ]Q-2^{D-1}, Q+2^{D-1}]. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLT0_SIZE_PACKED bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void polyt0_pack(uint8_t *r, const poly *a) { + uint32_t i; + uint32_t t[4]; + DBENCH_START(); + + for(i = 0; i < N/4; ++i) { + t[0] = Q + (1 << (D-1)) - a->coeffs[4*i+0]; + t[1] = Q + (1 << (D-1)) - a->coeffs[4*i+1]; + t[2] = Q + (1 << (D-1)) - a->coeffs[4*i+2]; + t[3] = Q + (1 << (D-1)) - a->coeffs[4*i+3]; + + r[7*i+0] = t[0]; + r[7*i+1] = t[0] >> 8; + r[7*i+1] |= t[1] << 6; + r[7*i+2] = t[1] >> 2; + r[7*i+3] = t[1] >> 10; + r[7*i+3] |= t[2] << 4; + r[7*i+4] = t[2] >> 4; + r[7*i+5] = t[2] >> 12; + r[7*i+5] |= t[3] << 2; + r[7*i+6] = t[3] >> 6; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyt0_unpack +* +* Description: Unpack polynomial t0 with coefficients in ]-2^{D-1}, 2^{D-1}]. +* Output coefficients lie in ]Q-2^{D-1},Q+2^{D-1}]. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void polyt0_unpack(poly *r, const uint8_t *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N/4; ++i) { + r->coeffs[4*i+0] = a[7*i+0]; + r->coeffs[4*i+0] |= (uint32_t)(a[7*i+1] & 0x3F) << 8; + + r->coeffs[4*i+1] = a[7*i+1] >> 6; + r->coeffs[4*i+1] |= (uint32_t)a[7*i+2] << 2; + r->coeffs[4*i+1] |= (uint32_t)(a[7*i+3] & 0x0F) << 10; + + r->coeffs[4*i+2] = a[7*i+3] >> 4; + r->coeffs[4*i+2] |= (uint32_t)a[7*i+4] << 4; + r->coeffs[4*i+2] |= (uint32_t)(a[7*i+5] & 0x03) << 12; + + r->coeffs[4*i+3] = a[7*i+5] >> 2; + r->coeffs[4*i+3] |= (uint32_t)a[7*i+6] << 6; + + r->coeffs[4*i+0] = Q + (1 << (D-1)) - r->coeffs[4*i+0]; + r->coeffs[4*i+1] = Q + (1 << (D-1)) - r->coeffs[4*i+1]; + r->coeffs[4*i+2] = Q + (1 << (D-1)) - r->coeffs[4*i+2]; + r->coeffs[4*i+3] = Q + (1 << (D-1)) - r->coeffs[4*i+3]; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyz_pack +* +* Description: Bit-pack polynomial z with coefficients +* in [-(GAMMA1 - 1), GAMMA1 - 1]. +* Input coefficients are assumed to be standard representatives. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLZ_SIZE_PACKED bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void polyz_pack(uint8_t *r, const poly *a) { +#if GAMMA1 > (1 << 19) +#error "polyz_pack() assumes GAMMA1 <= 2^{19}" +#endif + uint32_t i; + uint32_t t[2]; + DBENCH_START(); + + for(i = 0; i < N/2; ++i) { + /* Map to {0,...,2*GAMMA1 - 2} */ + t[0] = GAMMA1 - 1 - a->coeffs[2*i+0]; + t[0] += ((int32_t)t[0] >> 31) & Q; + t[1] = GAMMA1 - 1 - a->coeffs[2*i+1]; + t[1] += ((int32_t)t[1] >> 31) & Q; + + r[5*i+0] = t[0]; + r[5*i+1] = t[0] >> 8; + r[5*i+2] = t[0] >> 16; + r[5*i+2] |= t[1] << 4; + r[5*i+3] = t[1] >> 4; + r[5*i+4] = t[1] >> 12; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyz_unpack +* +* Description: Unpack polynomial z with coefficients +* in [-(GAMMA1 - 1), GAMMA1 - 1]. +* Output coefficients are standard representatives. +* +* Arguments: - poly *r: pointer to output polynomial +* - const uint8_t *a: byte array with bit-packed polynomial +**************************************************/ +void polyz_unpack(poly *r, const uint8_t *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N/2; ++i) { + r->coeffs[2*i+0] = a[5*i+0]; + r->coeffs[2*i+0] |= (uint32_t)a[5*i+1] << 8; + r->coeffs[2*i+0] |= (uint32_t)(a[5*i+2] & 0x0F) << 16; + + r->coeffs[2*i+1] = a[5*i+2] >> 4; + r->coeffs[2*i+1] |= (uint32_t)a[5*i+3] << 4; + r->coeffs[2*i+1] |= (uint32_t)a[5*i+4] << 12; + + r->coeffs[2*i+0] = GAMMA1 - 1 - r->coeffs[2*i+0]; + r->coeffs[2*i+0] += ((int32_t)r->coeffs[2*i+0] >> 31) & Q; + r->coeffs[2*i+1] = GAMMA1 - 1 - r->coeffs[2*i+1]; + r->coeffs[2*i+1] += ((int32_t)r->coeffs[2*i+1] >> 31) & Q; + } + + DBENCH_STOP(*tpack); +} + +/************************************************* +* Name: polyw1_pack +* +* Description: Bit-pack polynomial w1 with coefficients in [0, 15]. +* Input coefficients are assumed to be standard representatives. +* +* Arguments: - uint8_t *r: pointer to output byte array with at least +* POLW1_SIZE_PACKED bytes +* - const poly *a: pointer to input polynomial +**************************************************/ +void polyw1_pack(uint8_t *r, const poly *a) { + uint32_t i; + DBENCH_START(); + + for(i = 0; i < N/2; ++i) + r[i] = a->coeffs[2*i+0] | (a->coeffs[2*i+1] << 4); + + DBENCH_STOP(*tpack); +} +//#include +//#include "params.h" +//#include "poly.h" +//#include "polyvec.h" + +/**************************************************************/ +/************ Vectors of polynomials of length L **************/ +/**************************************************************/ + +/************************************************* +* Name: polyvecl_freeze +* +* Description: Reduce coefficients of polynomials in vector of length L +* to standard representatives. +* +* Arguments: - polyvecl *v: pointer to input/output vector +**************************************************/ +void polyvecl_freeze(polyvecl *v) { + uint32_t i; + + for(i = 0; i < L; ++i) + poly_freeze(v->vec+i); +} + +/************************************************* +* Name: polyvecl_add +* +* Description: Add vectors of polynomials of length L. +* No modular reduction is performed. +* +* Arguments: - polyvecl *w: pointer to output vector +* - const polyvecl *u: pointer to first summand +* - const polyvecl *v: pointer to second summand +**************************************************/ +void polyvecl_add(polyvecl *w, const polyvecl *u, const polyvecl *v) { + uint32_t i; + + for(i = 0; i < L; ++i) + poly_add(w->vec+i, u->vec+i, v->vec+i); +} + +/************************************************* +* Name: polyvecl_ntt +* +* Description: Forward NTT of all polynomials in vector of length L. Output +* coefficients can be up to 16*Q larger than input coefficients. +* +* Arguments: - polyvecl *v: pointer to input/output vector +**************************************************/ +void polyvecl_ntt(polyvecl *v) { + uint32_t i; + + for(i = 0; i < L; ++i) + poly_ntt(v->vec+i); +} + +/************************************************* +* Name: polyvecl_pointwise_acc_invmontgomery +* +* Description: Pointwise multiply vectors of polynomials of length L, multiply +* resulting vector by 2^{-32} and add (accumulate) polynomials +* in it. Input/output vectors are in NTT domain representation. +* Input coefficients are assumed to be less than 22*Q. Output +* coeffcient are less than 2*L*Q. +* +* Arguments: - poly *w: output polynomial +* - const polyvecl *u: pointer to first input vector +* - const polyvecl *v: pointer to second input vector +**************************************************/ +void polyvecl_pointwise_acc_invmontgomery(poly *w, + const polyvecl *u, + const polyvecl *v) +{ + uint32_t i; + poly t; + + poly_pointwise_invmontgomery(w, u->vec+0, v->vec+0); + + for(i = 1; i < L; ++i) { + poly_pointwise_invmontgomery(&t, u->vec+i, v->vec+i); + poly_add(w, w, &t); + } +} + +/************************************************* +* Name: polyvecl_chknorm +* +* Description: Check infinity norm of polynomials in vector of length L. +* Assumes input coefficients to be standard representatives. +* +* Arguments: - const polyvecl *v: pointer to vector +* - uint32_t B: norm bound +* +* Returns 0 if norm of all polynomials is strictly smaller than B and 1 +* otherwise. +**************************************************/ +int polyvecl_chknorm(const polyvecl *v, uint32_t bound) { + uint32_t i; + int ret = 0; + + for(i = 0; i < L; ++i) + ret |= poly_chknorm(v->vec+i, bound); + + return ret; +} + +/**************************************************************/ +/************ Vectors of polynomials of length K **************/ +/**************************************************************/ + + +/************************************************* +* Name: polyveck_reduce +* +* Description: Reduce coefficients of polynomials in vector of length K +* to representatives in [0,2*Q[. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void polyveck_reduce(polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_reduce(v->vec+i); +} + +/************************************************* +* Name: polyveck_csubq +* +* Description: For all coefficients of polynomials in vector of length K +* subtract Q if coefficient is bigger than Q. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void polyveck_csubq(polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_csubq(v->vec+i); +} + +/************************************************* +* Name: polyveck_freeze +* +* Description: Reduce coefficients of polynomials in vector of length K +* to standard representatives. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void polyveck_freeze(polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_freeze(v->vec+i); +} + +/************************************************* +* Name: polyveck_add +* +* Description: Add vectors of polynomials of length K. +* No modular reduction is performed. +* +* Arguments: - polyveck *w: pointer to output vector +* - const polyveck *u: pointer to first summand +* - const polyveck *v: pointer to second summand +**************************************************/ +void polyveck_add(polyveck *w, const polyveck *u, const polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_add(w->vec+i, u->vec+i, v->vec+i); +} + +/************************************************* +* Name: polyveck_sub +* +* Description: Subtract vectors of polynomials of length K. +* Assumes coefficients of polynomials in second input vector +* to be less than 2*Q. No modular reduction is performed. +* +* Arguments: - polyveck *w: pointer to output vector +* - const polyveck *u: pointer to first input vector +* - const polyveck *v: pointer to second input vector to be +* subtracted from first input vector +**************************************************/ +void polyveck_sub(polyveck *w, const polyveck *u, const polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_sub(w->vec+i, u->vec+i, v->vec+i); +} + +/************************************************* +* Name: polyveck_shiftl +* +* Description: Multiply vector of polynomials of Length K by 2^k without modular +* reduction. Assumes input coefficients to be less than 2^{32-k}. +* +* Arguments: - polyveck *v: pointer to input/output vector +* - uint32_t k: exponent +**************************************************/ +void polyveck_shiftl(polyveck *v, uint32_t k) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_shiftl(v->vec+i, k); +} + +/************************************************* +* Name: polyveck_ntt +* +* Description: Forward NTT of all polynomials in vector of length K. Output +* coefficients can be up to 16*Q larger than input coefficients. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void polyveck_ntt(polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_ntt(v->vec+i); +} + +/************************************************* +* Name: polyveck_invntt_montgomery +* +* Description: Inverse NTT and multiplication by 2^{32} of polynomials +* in vector of length K. Input coefficients need to be less +* than 2*Q. +* +* Arguments: - polyveck *v: pointer to input/output vector +**************************************************/ +void polyveck_invntt_montgomery(polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_invntt_montgomery(v->vec+i); +} + +/************************************************* +* Name: polyveck_chknorm +* +* Description: Check infinity norm of polynomials in vector of length K. +* Assumes input coefficients to be standard representatives. +* +* Arguments: - const polyveck *v: pointer to vector +* - uint32_t B: norm bound +* +* Returns 0 if norm of all polynomials are strictly smaller than B and 1 +* otherwise. +**************************************************/ +int polyveck_chknorm(const polyveck *v, uint32_t bound) { + uint32_t i; + int ret = 0; + + for(i = 0; i < K; ++i) + ret |= poly_chknorm(v->vec+i, bound); + + return ret; +} + +/************************************************* +* Name: polyveck_power2round +* +* Description: For all coefficients a of polynomials in vector of length K, +* compute a0, a1 such that a mod Q = a1*2^D + a0 +* with -2^{D-1} < a0 <= 2^{D-1}. Assumes coefficients to be +* standard representatives. +* +* Arguments: - polyveck *v1: pointer to output vector of polynomials with +* coefficients a1 +* - polyveck *v0: pointer to output vector of polynomials with +* coefficients Q + a0 +* - const polyveck *v: pointer to input vector +**************************************************/ +void polyveck_power2round(polyveck *v1, polyveck *v0, const polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_power2round(v1->vec+i, v0->vec+i, v->vec+i); +} + +/************************************************* +* Name: polyveck_decompose +* +* Description: For all coefficients a of polynomials in vector of length K, +* compute high and low bits a0, a1 such a mod Q = a1*ALPHA + a0 +* with -ALPHA/2 < a0 <= ALPHA/2 except a1 = (Q-1)/ALPHA where we +* set a1 = 0 and -ALPHA/2 <= a0 = a mod Q - Q < 0. +* Assumes coefficients to be standard representatives. +* +* Arguments: - polyveck *v1: pointer to output vector of polynomials with +* coefficients a1 +* - polyveck *v0: pointer to output vector of polynomials with +* coefficients Q + a0 +* - const polyveck *v: pointer to input vector +**************************************************/ +void polyveck_decompose(polyveck *v1, polyveck *v0, const polyveck *v) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_decompose(v1->vec+i, v0->vec+i, v->vec+i); +} + +/************************************************* +* Name: polyveck_make_hint +* +* Description: Compute hint vector. +* +* Arguments: - polyveck *h: pointer to output vector +* - const polyveck *u: pointer to first input vector +* - const polyveck *u: pointer to second input vector +* +* Returns number of 1 bits. +**************************************************/ +uint32_t polyveck_make_hint(polyveck *h, + const polyveck *u, + const polyveck *v) +{ + uint32_t i, s = 0; + + for(i = 0; i < K; ++i) + s += poly_make_hint(h->vec+i, u->vec+i, v->vec+i); + + return s; +} + +/************************************************* +* Name: polyveck_use_hint +* +* Description: Use hint vector to correct the high bits of input vector. +* +* Arguments: - polyveck *w: pointer to output vector of polynomials with +* corrected high bits +* - const polyveck *u: pointer to input vector +* - const polyveck *h: pointer to input hint vector +**************************************************/ +void polyveck_use_hint(polyveck *w, const polyveck *u, const polyveck *h) { + uint32_t i; + + for(i = 0; i < K; ++i) + poly_use_hint(w->vec+i, u->vec+i, h->vec+i); +} +//#include +//#include "params.h" +//#include "reduce.h" + +/************************************************* +* Name: montgomery_reduce +* +* Description: For finite field element a with 0 <= a <= Q*2^32, +* compute r \equiv a*2^{-32} (mod Q) such that 0 <= r < 2*Q. +* +* Arguments: - uint64_t: finite field element a +* +* Returns r. +**************************************************/ +uint32_t montgomery_reduce(uint64_t a) { + uint64_t t; + + t = a * QINV; + t &= (1ULL << 32) - 1; + t *= Q; + t = a + t; + t >>= 32; + return t; +} + +/************************************************* +* Name: reduce32 +* +* Description: For finite field element a, compute r \equiv a (mod Q) +* such that 0 <= r < 2*Q. +* +* Arguments: - uint32_t: finite field element a +* +* Returns r. +**************************************************/ +uint32_t reduce32(uint32_t a) { + uint32_t t; + + t = a & 0x7FFFFF; + a >>= 23; + t += (a << 13) - a; + return t; +} + +/************************************************* +* Name: csubq +* +* Description: Subtract Q if input coefficient is bigger than Q. +* +* Arguments: - uint32_t: finite field element a +* +* Returns r. +**************************************************/ +uint32_t csubq(uint32_t a) { + a -= Q; + a += ((int32_t)a >> 31) & Q; + return a; +} + +/************************************************* +* Name: freeze +* +* Description: For finite field element a, compute standard +* representative r = a mod Q. +* +* Arguments: - uint32_t: finite field element a +* +* Returns r. +**************************************************/ +uint32_t freeze(uint32_t a) { + a = reduce32(a); + a = csubq(a); + return a; +} +//#include +//#include "params.h" + +/************************************************* +* Name: power2round +* +* Description: For finite field element a, compute a0, a1 such that +* a mod Q = a1*2^D + a0 with -2^{D-1} < a0 <= 2^{D-1}. +* Assumes a to be standard representative. +* +* Arguments: - uint32_t a: input element +* - uint32_t *a0: pointer to output element Q + a0 +* +* Returns a1. +**************************************************/ +uint32_t power2round(uint32_t a, uint32_t *a0) { + int32_t t; + + /* Centralized remainder mod 2^D */ + t = a & ((1 << D) - 1); + t -= (1 << (D-1)) + 1; + t += (t >> 31) & (1 << D); + t -= (1 << (D-1)) - 1; + *a0 = Q + t; + a = (a - t) >> D; + return a; +} + +/************************************************* +* Name: decompose +* +* Description: For finite field element a, compute high and low bits a0, a1 such +* that a mod Q = a1*ALPHA + a0 with -ALPHA/2 < a0 <= ALPHA/2 except +* if a1 = (Q-1)/ALPHA where we set a1 = 0 and +* -ALPHA/2 <= a0 = a mod Q - Q < 0. Assumes a to be standard +* representative. +* +* Arguments: - uint32_t a: input element +* - uint32_t *a0: pointer to output element Q + a0 +* +* Returns a1. +**************************************************/ +uint32_t decompose(uint32_t a, uint32_t *a0) { +#if ALPHA != (Q-1)/16 +#error "decompose assumes ALPHA == (Q-1)/16" +#endif + int32_t t, u; + + /* Centralized remainder mod ALPHA */ + t = a & 0x7FFFF; + t += (a >> 19) << 9; + t -= ALPHA/2 + 1; + t += (t >> 31) & ALPHA; + t -= ALPHA/2 - 1; + a -= t; + + /* Divide by ALPHA (possible to avoid) */ + u = a - 1; + u >>= 31; + a = (a >> 19) + 1; + a -= u & 1; + + /* Border case */ + *a0 = Q + t - (a >> 4); + a &= 0xF; + return a; +} + +/************************************************* +* Name: make_hint +* +* Description: Compute hint bit indicating whether or not high bits of two +* finite field elements differ. Assumes input elements to be +* standard representatives. +* +* Arguments: - uint32_t a: first input element +* - uint32_t b: second input element +* +* Returns 1 if high bits of a and b differ and 0 otherwise. +**************************************************/ +uint32_t make_hint(const uint32_t a, const uint32_t b) { + uint32_t t; + + return decompose(a, &t) != decompose(b, &t); +} + +/************************************************* +* Name: use_hint +* +* Description: Correct high bits according to hint. +* +* Arguments: - uint32_t a: input element +* - uint32_t hint: hint bit +* +* Returns corrected high bits. +**************************************************/ +uint32_t use_hint(const uint32_t a, const uint32_t hint) { + uint32_t a0, a1; + + a1 = decompose(a, &a0); + if(hint == 0) + return a1; + else if(a0 > Q) + return (a1 + 1) & 0xF; + else + return (a1 - 1) & 0xF; + + /* If decompose does not divide out ALPHA: + if(hint == 0) + return a1; + else if(a0 > Q) + return (a1 + ALPHA) % (Q - 1); + else + return (a1 - ALPHA) % (Q - 1); + */ +} +//#include +//#include "params.h" +//#include "sign.h" +//#include "randombytes.h" +//#include "fips202.h" +//#include "poly.h" +//#include "polyvec.h" +//#include "packing.h" +#ifdef STANDALONE +#ifdef _WIN32 +#include +void randombytes(unsigned char *x,long xlen) +{ + HCRYPTPROV prov = 0; + CryptAcquireContextW(&prov, NULL, NULL,PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); + CryptGenRandom(prov, xlen, x); + CryptReleaseContext(prov, 0); +} +#else +#include +#include +#include +void randombytes(unsigned char *x,long xlen) +{ + static int fd = -1; + int32_t i; + if (fd == -1) { + for (;;) { + fd = open("/dev/urandom",O_RDONLY); + if (fd != -1) break; + sleep(1); + } + } + while (xlen > 0) { + if (xlen < 1048576) i = (int32_t)xlen; else i = 1048576; + i = (int32_t)read(fd,x,i); + if (i < 1) { + sleep(1); + continue; + } + if ( 0 ) + { + int32_t j; + for (j=0; j %p\n",x); + } + x += i; + xlen -= i; + } +} +#endif +#endif + +/************************************************* +* Name: expand_mat +* +* Description: Implementation of ExpandA. Generates matrix A with uniformly +* random coefficients a_{i,j} by performing rejection +* sampling on the output stream of SHAKE128(rho|i|j). +* +* Arguments: - polyvecl mat[K]: output matrix +* - const uint8_t rho[]: byte array containing seed rho +**************************************************/ +void expand_mat(polyvecl mat[K], const uint8_t rho[SEEDBYTES]) { + uint32_t i, j; + uint8_t inbuf[SEEDBYTES + 1]; + /* Don't change this to smaller values, + * sampling later assumes sufficient SHAKE output! + * Probability that we need more than 5 blocks: < 2^{-132}. + * Probability that we need more than 6 blocks: < 2^{-546}. */ + uint8_t outbuf[5*SHAKE128_RATE]; + + for(i = 0; i < SEEDBYTES; ++i) + inbuf[i] = rho[i]; + + for(i = 0; i < K; ++i) { + for(j = 0; j < L; ++j) { + inbuf[SEEDBYTES] = i + (j << 4); + shake128(outbuf, sizeof(outbuf), inbuf, SEEDBYTES + 1); + poly_uniform(mat[i].vec+j, outbuf); + } + } +} + +/************************************************* +* Name: challenge +* +* Description: Implementation of H. Samples polynomial with 60 nonzero +* coefficients in {-1,1} using the output stream of +* SHAKE256(mu|w1). +* +* Arguments: - poly *c: pointer to output polynomial +* - const uint8_t mu[]: byte array containing mu +* - const polyveck *w1: pointer to vector w1 +**************************************************/ +void challenge(poly *c, + const uint8_t mu[CRHBYTES], + const polyveck *w1) +{ + uint32_t i, b, pos; + uint8_t inbuf[CRHBYTES + K*POLW1_SIZE_PACKED]; + uint8_t outbuf[SHAKE256_RATE]; + uint64_t state[25], signs, mask; + + for(i = 0; i < CRHBYTES; ++i) + inbuf[i] = mu[i]; + for(i = 0; i < K; ++i) + polyw1_pack(inbuf + CRHBYTES + i*POLW1_SIZE_PACKED, w1->vec+i); + + shake256_absorb(state, inbuf, sizeof(inbuf)); + shake256_squeezeblocks(outbuf, 1, state); + + signs = 0; + for(i = 0; i < 8; ++i) + signs |= (uint64_t)outbuf[i] << 8*i; + + pos = 8; + mask = 1; + + for(i = 0; i < N; ++i) + c->coeffs[i] = 0; + + for(i = 196; i < 256; ++i) { + do { + if(pos >= SHAKE256_RATE) { + shake256_squeezeblocks(outbuf, 1, state); + pos = 0; + } + + b = outbuf[pos++]; + } while(b > i); + + c->coeffs[i] = c->coeffs[b]; + c->coeffs[b] = (signs & mask) ? Q - 1 : 1; + mask <<= 1; + } +} + +/************************************************* +* Name: _dilithium_keypair +* +* Description: Generates public and private key. +* +* Arguments: - uint8_t *pk: pointer to output public key (allocated +* array of CRYPTO_PUBLICKEYBYTES bytes) +* - uint8_t *sk: pointer to output private key (allocated +* array of CRYPTO_SECRETKEYBYTES bytes) +* +* Returns 0 (success) +**************************************************/ +int _dilithium_keypair(uint8_t *pk, uint8_t *sk,uint8_t *privkey) +{ + uint32_t i; + uint8_t seedbuf[3*SEEDBYTES]; + uint8_t tr[CRHBYTES]; + uint8_t *rho, *rhoprime, *key; + uint16_t nonce = 0; + polyvecl mat[K]; + polyvecl s1, s1hat; + polyveck s2, t, t1, t0; + + /* Expand 32 bytes of randomness into rho, rhoprime and key */ + //randombytes(seedbuf, SEEDBYTES); + memcpy(seedbuf,privkey,SEEDBYTES); + shake256(seedbuf, 3*SEEDBYTES, seedbuf, SEEDBYTES); + rho = seedbuf; + rhoprime = rho + SEEDBYTES; + key = rho + 2*SEEDBYTES; + + /* Expand matrix */ + expand_mat(mat, rho); + + /* Sample short vectors s1 and s2 */ + for(i = 0; i < L; ++i) + poly_uniform_eta(s1.vec+i, rhoprime, nonce++); + for(i = 0; i < K; ++i) + poly_uniform_eta(s2.vec+i, rhoprime, nonce++); + + /* Matrix-vector multiplication */ + s1hat = s1; + polyvecl_ntt(&s1hat); + for(i = 0; i < K; ++i) { + polyvecl_pointwise_acc_invmontgomery(t.vec+i, mat+i, &s1hat); + poly_reduce(t.vec+i); + poly_invntt_montgomery(t.vec+i); + } + + /* Add noise vector s2 */ + polyveck_add(&t, &t, &s2); + + /* Extract t1 and write public key */ + polyveck_freeze(&t); + polyveck_power2round(&t1, &t0, &t); + pack_pk(pk, rho, &t1); + + /* Compute CRH(rho, t1) and write secret key */ + shake256(tr, CRHBYTES, pk, CRYPTO_PUBLICKEYBYTES); + pack_sk(sk, rho, key, tr, &s1, &s2, &t0); + + return 0; +} + +/************************************************* +* Name: _dilithium_sign +* +* Description: Compute signed message. +* +* Arguments: - uint8_t *sm: pointer to output signed message (allocated +* array with CRYPTO_BYTES + mlen bytes), +* can be equal to m +* - int32_t *smlen: pointer to output length of signed +* message +* - const uint8_t *m: pointer to message to be signed +* - int32_t mlen: length of message +* - const uint8_t *sk: pointer to bit-packed secret key +* +* Returns 0 (success) +**************************************************/ +int _dilithium_sign(uint8_t *sm, + int32_t *smlen, + const uint8_t *m, + int32_t mlen, + const uint8_t *sk) +{ + int32_t i, j; + uint32_t n; + uint8_t seedbuf[2*SEEDBYTES + CRHBYTES]; // TODO: nonce in seedbuf (2x) + uint8_t tr[CRHBYTES]; + uint8_t *rho, *key, *mu; + uint16_t nonce = 0; + poly c, chat; + polyvecl mat[K], s1, y, yhat, z; + polyveck s2, t0, w, w1; + polyveck h, wcs2, wcs20, ct0, tmp; + + rho = seedbuf; + key = seedbuf + SEEDBYTES; + mu = seedbuf + 2*SEEDBYTES; + unpack_sk(rho, key, tr, &s1, &s2, &t0, sk); + + /* Copy tr and message into the sm buffer, + * backwards since m and sm can be equal in SUPERCOP API */ + for(i = 1; i <= mlen; ++i) + sm[CRYPTO_BYTES + mlen - i] = m[mlen - i]; + for(i = 0; i < CRHBYTES; ++i) + sm[CRYPTO_BYTES - CRHBYTES + i] = tr[i]; + + /* Compute CRH(tr, msg) */ + shake256(mu, CRHBYTES, sm + CRYPTO_BYTES - CRHBYTES, CRHBYTES + mlen); + + /* Expand matrix and transform vectors */ + expand_mat(mat, rho); + polyvecl_ntt(&s1); + polyveck_ntt(&s2); + polyveck_ntt(&t0); + + rej: + /* Sample intermediate vector y */ + for(i = 0; i < L; ++i) + poly_uniform_gamma1m1(y.vec+i, key, nonce++); + + /* Matrix-vector multiplication */ + yhat = y; + polyvecl_ntt(&yhat); + for(i = 0; i < K; ++i) { + polyvecl_pointwise_acc_invmontgomery(w.vec+i, mat+i, &yhat); + poly_reduce(w.vec+i); + poly_invntt_montgomery(w.vec+i); + } + + /* Decompose w and call the random oracle */ + polyveck_csubq(&w); + polyveck_decompose(&w1, &tmp, &w); + challenge(&c, mu, &w1); + + /* Compute z, reject if it reveals secret */ + chat = c; + poly_ntt(&chat); + for(i = 0; i < L; ++i) { + poly_pointwise_invmontgomery(z.vec+i, &chat, s1.vec+i); + poly_invntt_montgomery(z.vec+i); + } + polyvecl_add(&z, &z, &y); + polyvecl_freeze(&z); + if(polyvecl_chknorm(&z, GAMMA1 - BETA)) + goto rej; + + /* Compute w - cs2, reject if w1 can not be computed from it */ + for(i = 0; i < K; ++i) { + poly_pointwise_invmontgomery(wcs2.vec+i, &chat, s2.vec+i); + poly_invntt_montgomery(wcs2.vec+i); + } + polyveck_sub(&wcs2, &w, &wcs2); + polyveck_freeze(&wcs2); + polyveck_decompose(&tmp, &wcs20, &wcs2); + polyveck_csubq(&wcs20); + if(polyveck_chknorm(&wcs20, GAMMA2 - BETA)) + goto rej; + + for(i = 0; i < K; ++i) + for(j = 0; j < N; ++j) + if(tmp.vec[i].coeffs[j] != w1.vec[i].coeffs[j]) + goto rej; + + /* Compute hints for w1 */ + for(i = 0; i < K; ++i) { + poly_pointwise_invmontgomery(ct0.vec+i, &chat, t0.vec+i); + poly_invntt_montgomery(ct0.vec+i); + } + + polyveck_csubq(&ct0); + if(polyveck_chknorm(&ct0, GAMMA2)) + goto rej; + + polyveck_add(&tmp, &wcs2, &ct0); + polyveck_csubq(&tmp); + n = polyveck_make_hint(&h, &wcs2, &tmp); + if(n > OMEGA) + goto rej; + + /* Write signature */ + pack_sig(sm, &z, &h, &c); + + *smlen = mlen + CRYPTO_BYTES; + return 0; +} + +/************************************************* +* Name: _dilithium_verify +* +* Description: Verify signed message. +* +* Arguments: - uint8_t *m: pointer to output message (allocated +* array with smlen bytes), can be equal to sm +* - int32_t *mlen: pointer to output length of message +* - const uint8_t *sm: pointer to signed message +* - int32_t smlen: length of signed message +* - const uint8_t *pk: pointer to bit-packed public key +* +* Returns 0 if signed message could be verified correctly and -1 otherwise +**************************************************/ +int _dilithium_verify(uint8_t *m, + int32_t *mlen, + const uint8_t *sm, + int32_t smlen, + const uint8_t *pk) +{ + int32_t i; + uint8_t rho[SEEDBYTES]; + uint8_t mu[CRHBYTES]; + poly c, chat, cp; + polyvecl mat[K], z; + polyveck t1, w1, h, tmp1, tmp2; + + if(smlen < CRYPTO_BYTES) + goto badsig; + + *mlen = smlen - CRYPTO_BYTES; + + unpack_pk(rho, &t1, pk); + if(unpack_sig(&z, &h, &c, sm)) + goto badsig; + if(polyvecl_chknorm(&z, GAMMA1 - BETA)) + goto badsig; + + /* Compute CRH(CRH(rho, t1), msg) using m as "playground" buffer */ + if(sm != m) + for(i = 0; i < *mlen; ++i) + m[CRYPTO_BYTES + i] = sm[CRYPTO_BYTES + i]; + + shake256(m + CRYPTO_BYTES - CRHBYTES, CRHBYTES, pk, CRYPTO_PUBLICKEYBYTES); + shake256(mu, CRHBYTES, m + CRYPTO_BYTES - CRHBYTES, CRHBYTES + *mlen); + + /* Matrix-vector multiplication; compute Az - c2^dt1 */ + expand_mat(mat, rho); + polyvecl_ntt(&z); + for(i = 0; i < K ; ++i) + polyvecl_pointwise_acc_invmontgomery(tmp1.vec+i, mat+i, &z); + + chat = c; + poly_ntt(&chat); + polyveck_shiftl(&t1, D); + polyveck_ntt(&t1); + for(i = 0; i < K; ++i) + poly_pointwise_invmontgomery(tmp2.vec+i, &chat, t1.vec+i); + + polyveck_sub(&tmp1, &tmp1, &tmp2); + polyveck_reduce(&tmp1); + polyveck_invntt_montgomery(&tmp1); + + /* Reconstruct w1 */ + polyveck_csubq(&tmp1); + polyveck_use_hint(&w1, &tmp1, &h); + + /* Call random oracle and verify challenge */ + challenge(&cp, mu, &w1); + for(i = 0; i < N; ++i) + if(c.coeffs[i] != cp.coeffs[i]) + { + /* Signature verification failed */ + badsig: + *mlen = (int32_t) -1; + for(i = 0; i < smlen; ++i) + m[i] = 0; + + return -1; + } + + /* All good, copy msg, return 0 */ + for(i = 0; i < *mlen; ++i) + m[i] = sm[CRYPTO_BYTES + i]; + return 0; +} + +#ifdef STANDALONE +/////////////////////////////////////////////////////////////////////////////// +#include +#include + +#define MLEN 59 +#define NTESTS 10000 + +int64_t timing_overhead; +#ifdef DBENCH +int64_t *tred, *tadd, *tmul, *tround, *tsample, *tpack, *tshake; +#endif + +static int cmp_llu(const void *a, const void*b) +{ + if(*(int64_t *)a < *(int64_t *)b) return -1; + else if(*(int64_t *)a > *(int64_t *)b) return 1; + //else if ( (uint64_t)a < (uint64_t)b ) return -1; + //else return 1; + return(0); +} + +static int64_t median(int64_t *l, size_t llen) +{ + qsort(l,llen,sizeof(uint64_t),cmp_llu); + + if(llen%2) return l[llen/2]; + else return (l[llen/2-1]+l[llen/2])/2; +} + +static int64_t average(int64_t *t, size_t tlen) +{ + uint64_t acc=0; + size_t i; + for(i=0;i from above -> pubtxid 9d856b2be6e54c8f04ae3f86aef722b0535180b3e9eb926c53740e481a1715f9 + + now test signing some random 32 byte message + + cclib sign 19 \"[%22aff51dad774a1c612dc82e63f85f07b992b665836b0f0efbcb26ee679f4f4848%22]\" + { + "warning": "test mode using privkey for -pubkey, only for testing. there is no point using quantum secure signing if you are using a privkey with a known secp256k1 pubkey!!", + "msg32": "aff51dad774a1c612dc82e63f85f07b992b665836b0f0efbcb26ee679f4f4848", + "pkaddr": "PNoTcVH8G5TBTQigyVZTsaMMNYYRvywUNu", + "skaddr": "SejsccjwGrZKaziD1kpfgQhXA32xvzP75i", + "signature": "be067f4bd81b9b0b772e0e2872cc086f6c2ff4c558a465afe80ab71c2c7b39a25ad8300629337c022d8c477cf7728cd11a3f6135bccfdbd68de5cd4517e70a70ec3b836041dc9c2f1abed65f2519e43a31ca6ad4991ce98460a14ee70d28c47f5a1d967c25b1ac93afea7e2b11...836b0f0efbcb26ee679f4f4848", + "sighash": "cfed6d7f059b87635bde6cb31accd736bf99ff3d" + } + + it is a very big signature, but that seems to be dilithium sig size. let us verify it: + + cclib verify 19 \"[%229d856b2be6e54c8f04ae3f86aef722b0535180b3e9eb926c53740e481a1715f9%22,%22aff51dad774a1c612dc82e63f85f07b992b665836b0f0efbcb26ee679f4f4848%22,%22be067f4bd81b9b0b772e0e2872cc086f6c2ff4c558a465afe80ab71c2c7b39a25ad8300629337c022d8c477cf7728cd11a3f6135bccfdbd68de5cd4517e70a70ec3b836041dc9c2f1abed65f2519e43a31ca6ad4991ce98460a14ee70d28c47f5a1d967c25b1ac93afea7e2b11aa2fb715ac08bd3eac739425c67974ecd682f711a0b175b30278febfe55586650ed8b0098de745944450a6836b6ab23e0c5ebdd7503188428c3159f1671ca27d9d529d344d246e116b2001dbba085afe1bfcdd12d88ae2efbcead268b10cec4f76531aba594887dd239b59c4c676b348a56a1cc2e0032590c74513cfba7f03f8b6d7a14bb6f6a16ae743317ecd8551b3362dc892bcae550032682d130772f65b2e96a5ad4ce2b8e9a41a48c2a52c80f349c99dc110807e7c662f7ef960f628001ca9a9f249b53b23c4680e3a6acec89e3c26d0265b617353654f55a752f9ea3689570c068a414793c3575fae66f6fa425ce282a574981228a52e2ede14fbde3ac66a8e061a538bee737d17fbb48afc39cd914518ef2a182ce1feb66b1a8bf9934b6fef491f2bd3598e3421399fe11754bc61e149e8846f74d44d96c7dc47f06d04d6c09dc2b2c9d78e76a713722eec637f8e3fb5cd5adfd8ba2ce05dacdf2f9522e89bff2ee745d49873755a0079835e982c6c55fd9a96597505d79090da8df4feb422422b1d6427fde4242aafcb6ed581d8e4ffd722daf56fd45b017a2a2fa2f4e30a3a457686bdd184505461fc6749e4a20b7faa2a1d9a295a445ea564b84c1b820d9cf5c06142353671f989565a3767bd6ddabfc3bf1368acdae8870580f21baa2093cea4447688e35719bd78c785821f944ecc9a093f9a65bf2584f1a0c68f70f11f2485e02f288c2c8b6692883983607960aa16065d22082121f6fd6588f07cd3fb57bba624fbb9c7077cb1400fe4edf48156b7622fab70cce1cbd17bde2f4c24b9a86d485727df413e06a6c31cab27284a69fd46e00fc6e80872ed5291b598c74964488ffdb19d0dc94fce37db3f5230d947cb4d83ae55e0357aab1ec86b63fe606f86a77aa78fc4fe986be450b74f1ffbb5ba9eeaea11c7c7ffa6d87a9d49767ce761614bd6cc5df3767ed6396b84354a9634bb3e35606e961fc023504473bf3b7e13244f19d1dee101af1854f80899f95409bb402a5267ad21ddba80e2dd0dd513d0fc88067ac4078e69c12bd19807c03a916d2a42cdbe7b4cdac4bc2314fe3369723d16c30bf277db823c1457f5ff64f3117b82b991ee8b65b7e6b8f7814a15b4ca8cebe88d12236cf1b7dd06b75cab506d78c2072fddf2002be366f43ca68866f87fe9a56808ab7f82aa925091e1f0fba371642039316939446b769973a9c93efe3104699ad3eceac89eb1c2507b65b43d21388f93ff28b194110d7114b97a10cb212515127ede0287d455791e1c6d554b0d8a4e75f2701bc3430786cc69081dbd96a73a308fc6a60fc773fdc7df49b1865c3e989f2a528872fd4c1715dadb11c801c1492ce07bde59e25a801bb542e2caef35f99ca4cb0a3f1d2c2c6e3895c94001a0b2cc648057c2e44c780655f93d56a2cd62a9d55eb8de45e9ec75bfa3d121223aba700062ba3f54162fb9ba136aca6aeb119bca9a0d6bf18e89f54d9ff09c6a2036f767098fbbaf20e10db25e43386ecda201c05e794805269f1a77e50657052d16ae1e154d706a7fa81c419b9d262766e8edb8fd6343f509bed44098ef741f10a6206474c3490354695762a5a4532dd0279abc38ef75a44899a5d8d0e77af638aedd07071f37a3c5f82bbbd05a7b4c0e23d2fc3a5bbc40a52f588c8592f02fb30be56ae0990b24a80690c0b5c9df29549f7dec89f62920a37d05c62c27a62ee01fea164bf28937cdc7d3f2937a5756ada91c2615ce7ed20f0ed07cf486b76d0a63d193363567746eff0ff90ace3dbdfb770d55161c84ccdebca1a600337e7ffed0fdfbc041ed44e0014cced03d1af55ae9fa14d87d60dfe96ac7cde67a1d8ea2150c00ba5fb9a0ec0eff5bd9f734da71edbe7e2f71b6465984c411de8a3cc77a337b2ffdee6ab6d904a79316c15d15176401bc7e72fabb1e9571c7e7188ba09a295400437e4b96549d9827fba6d3493bc6f58f95e240b0a0159054014e5e3103e3af4eef77d3896290c7bf930edbe77615d56aa0a93034c92830c1382c0c06726d2ec7d6c2ed45d3a9fb9646892402812f1df9a003705d3f549d84f9ed3b5fe3c40fcb0bcb28a0d32f2fa27fcdb82509a0725d7314a3eb99a701169fae9e3dcdc2cc20d73aa8b2c5feb645556a8b946581e4e9e82f6a19a21f5babd35d49810dc88923c4908eca3690b774f367a41c3a37b54af9847d73a7eed1ee45edaaed0f316d551c08e3e642cebc97ce71a811664ee9296e7fedffb90011cc353302acd931bc0d152d7e6332a8f0d71059987c3b90f3f57178dec3f30c58ded0bc80eb65b0c9b8d16ec73ebe17e31259181b2376405db17e279419f1c685ad71b6cc91c81a120de2db2c532e67bc3a58d22b549fae61f32398d03cb1f5e245cfec65c40c9dfd0b8a93812f67840c653c5304402a1ff6189fd24f8ce3482e5cf92b3581445009c3b586bb421459ce9457868787c78b787bd45df7e55c3165a92194d38b913a6ef6f31af4c2afcfd0158eb8eb2820f7d41e3efca9367528a0b6fe6ec3fd01082bc60a9fe2a13ab3705b3b0c07173d4d762c8de4b6598d30b97e32339aeb706de47170e1033603267c6ce8caa2977990cbda75984de4e5ede6e36ff889b53b2cbbebc37f9e56e78c62ff856bcb27aba8892ace8fcaae09b31d7f5f850596014e868003d632c9dc12e7c83f6de676d9ae4328862326572e2e0353d5547f7f73fdf5b0227b6d108ae28e3dc622d5ac3dcf98bd1461917d78468ac2912329027c1085611dce7a6b7b3fa8451a5c3c6b448b1b9ad9dd84308991e4688595bcb289ec4b99f63db0c18969bd4b5cdc14d85007d683f936ab3207b59e3971f86f8fb388e72bdc7c9fb3b466061223e85138ee6a5657e8862ca51819c9d92b339ac6900e9f60a71d4a1eb09707cedc32bb477c91a8b5792e850606e1de57122d017a2025423d40b48e0bbe711ec03381630b9003ff55e10ac6f0031dfc54ed54ccd3309abd17ee026958fdf23bb74d53b84d8e2ef150fb2216265454c5f6446e221ab1c95c086571cad14251f618c9c58a9dfb83f9a8c58c9c5c026b9bc8f90860acde16557c064f95b178a9776e463b2d7d658e4acfa1ea30c429c0b813a5872b02d7b0bafcc095e979f737834933fbbf1220c05a0b0346f5932c669c534e22ab5ab42c39fd0e062abff05a2d34060e6f539c7ae9244903d981095fac6cff5d20ac9d298de27cb1ea7079d6dcc47504f988e3bdd1c48ca23f9ec305950459446c51b879a62e75cbc3570d2dbf93594f299111e27b60e5193d6e766a40130ee5d33a43eb43aba5c5701de878fdeaa16c998607e7fbf6c8827cb1f914db9d73c6ae48a0cb416218cc50b335f171e4df050561dfb1669939ccf2c498ff1d8f53a7d7c77195348502c4ffd5c18362f4eb4c3077e504853ff1e84c6166e1f889781bf5dcccf0daf8ac0881ee7202650abdff8d6cda2f8bf3b6a96d23f5ffa0104ee72dd1e8ae7cd08258d36b50cb40048756216845815a3e01efd33d5fae86a0680920422325893296dcb2af0d6df21c7193e387092b61408aac63df4a79c3b1e54869ba3c43ae2a54446e64053c061dd8bb3e132be46d9a83b6675791f49aa9617345801e97be7f4f7159ba1d7da623c7868ad281ddbb0f75fec7fe56ff0a44a8ac3b51a1f784b2b039d6434f92d3254fd83b4221ca18883637a0eb12217ebc8e149681c21e0edbd11289cfa7f78d536d8858a60056b8c28916e1d34ce1a6d344034b2e72162a5fc92b137354c2b791e7ad6ee4679f71181188ba69c9ded078421885a6cc18bc58c383d190c11d236e53eaa39a99d157e4dd74bc4aa2ce1354511128d6b407007dbcaeb9c3b712ed2b334de23c66735f534a9dddb7ab2d06c6a4669d2bd38c8c812b287b39b3591ac77e617834ea7c4c38b1133f2cafdf51f9afca7f44e9b527d3e0e840b05ec8bf57fcceb8a28546a3593ff1b94ee6a8d7d28b8e6007d0ea7da80552e4382b3ff3b6152175083717f42c5c902131b0a27e23bbcf4ba03140a6dc3bcccbc8ca93ba6161fe3c36a1835e9e02695bac571a07f6b2267998213aa0c4c7b93c2ed3a58e12cab5a51edf462a30df14e7e32727b4da1f7f29e9ea30f65ab090b22e9ae00ae9419bf26a44482d536812e2b4c2e1fd2af622d827b04b67eac1052d2ccee68207b3b6ca3d96bc4de4039a3a3e50c58a17786edb08caad6091dab0e7beffd0acb748d5c5ef6a171d8d113c7c310f18712a53607dbf01653157090cdd19c5845c1b7e11a4a61c2229cbb1e6927c74f187964c646b007051841b1b83e670611c1e9eb0b2406ee432122613a4c7e9f60c2cf8db2d6032225604c1d5468b1e90bb57651c2223363743516164a4aab0b4bac2d70d1a254f687384889daee2fc2d32365d78878b8c9aabbbbcc8d7f4fb191d23283f4d5359767e8c99a1b8c8cddfe5040c1e2339606e788ca9cad6f2fc0712236a70c9cdd6fb0000000000000000000000000000000000000000000000000000000000000000000000101c2c3e4c55b80404422409560084401072601824140801b8244ae84401008080081022408cdea5834e5fd1220daff51dad774a1c612dc82e63f85f07b992b665836b0f0efbcb26ee679f4f4848%22]\" + { + "sighash": "cfed6d7f059b87635bde6cb31accd736bf99ff3d", + "msg32": "aff51dad774a1c612dc82e63f85f07b992b665836b0f0efbcb26ee679f4f4848", + "handle": "jl777", + "pkaddr": "PNoTcVH8G5TBTQigyVZTsaMMNYYRvywUNu", + "result": "success" + } + + the basics are working, now it is time to send and spend + + cclib send 19 \"[%22jl777%22,%229d856b2be6e54c8f04ae3f86aef722b0535180b3e9eb926c53740e481a1715f9%22,7.77]\" + { + "handle": "jl777", + "hex": "0400008085202f8901ff470ca3fb4f935a32dd312db801dcabce0e8b49c7774bb4f1d39a45b3a68bab0100000049483045022100d1c29d5f870dd18aa865e12632fa0cc8df9a8a770a23360e9c443d39cb141c5f0220304c7c77a6d711888d4bcb836530b6509eabe158496029b0bf57b5716f24beb101ffffffff034014502e00000000302ea22c8020b09ee47b12b5b9a2edcf0e7c4fb2a517b879eb88ac98b16185dfef476506b1dd8103120c008203000401cc3cd0ff7646070000232102aff51dad774a1c612dc82e63f85f07b992b665836b0f0efbcb26ee679f4f4848ac0000000000000000246a221378f915171a480e74536c92ebe9b3805153b022f7ae863fae048f4ce5e62b6b859d00000000120c00000000000000000000000000", + "txid": "4aac73ebe82c12665d1d005a0ae1a1493cb1e2c714680ef9d016f48a7c77b4a2", + "result": "success" + } + dont forget to broadcast it: 4aac73ebe82c12665d1d005a0ae1a1493cb1e2c714680ef9d016f48a7c77b4a2 + notice how small the tx is! 289 bytes as it is sent to the destpubtxid, which in turn contains the handle, pub33 and bigpub. the handle is used for error check, pub33 is used to make the destination CC address, so the normal CC signing needs to be passed in addition to the spend restrictions for dilithium. + + cclib spend 19 \"[%224aac73ebe82c12665d1d005a0ae1a1493cb1e2c714680ef9d016f48a7c77b4a2%22,%22210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac%22]\" + + this generates a really big hex, broadcast it and if all went well it will get confirmed. + a dilithium spend! + + to generate a seed that wont be directly derivable from an secp256k1 keypair, do: + cclib keypair 19 \"[%22rand%22]\" + + to do a Qsend (multiple dilithium inputs and outputs) + + cclib Qsend 19 \"[%22mypubtxid%22,%22%22,%22%22,0.777]\" + there can be up to 64 outputs, where each one can be a different destpubtxid or scriptPubKey. The only restriction is that scriptPubKey hex cant be 32 bytes. + + Qsend is able to spend many Qvins as long as they are for the same dilithium bigpub + secp pub33. And the outputs can be to many different Qvouts or normal vouts. This allows to keep funds totally within the dilithium system and also to transfer back to normal taddrs. Qsend currently only sends from Qfunds, though it could also use funds from normal inputs. + + Currently, to get funds from normal inputs to a dilithium, the send rpc can be used as above. So that provides a way to push funds into dilithium. The spend rpc becomes redundant with Qsend. + + To properly test this, we need to make sure that transactions Qsend can use send outputs, and Qsend outputs and a combination. Of course, it needs to be validated that funds are not lost, Qsends work properly, etc. + + */ + +#define DILITHIUM_TXFEE 10000 + +void calc_rmd160_sha256(uint8_t rmd160[20],uint8_t *data,int32_t datalen); +char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len); + +struct dilithium_handle +{ + UT_hash_handle hh; + uint256 destpubtxid; + char handle[32]; +} *Dilithium_handles; + +pthread_mutex_t DILITHIUM_MUTEX; + +struct dilithium_handle *dilithium_handlenew(char *handle) +{ + struct dilithium_handle *hashstr = 0; int32_t len = (int32_t)strlen(handle); + if ( len < sizeof(Dilithium_handles[0].handle)-1 ) + { + pthread_mutex_lock(&DILITHIUM_MUTEX); + HASH_FIND(hh,Dilithium_handles,handle,len,hashstr); + if ( hashstr == 0 ) + { + hashstr = (struct dilithium_handle *)calloc(1,sizeof(*hashstr)); + strncpy(hashstr->handle,handle,sizeof(hashstr->handle)); + HASH_ADD_KEYPTR(hh,Dilithium_handles,hashstr->handle,len,hashstr); + } + pthread_mutex_unlock(&DILITHIUM_MUTEX); + } + return(hashstr); +} + +struct dilithium_handle *dilithium_handlefind(char *handle) +{ + struct dilithium_handle *hashstr = 0; int32_t len = (int32_t)strlen(handle); + if ( len < sizeof(Dilithium_handles[0].handle)-1 ) + { + pthread_mutex_lock(&DILITHIUM_MUTEX); + HASH_FIND(hh,Dilithium_handles,handle,len,hashstr); + pthread_mutex_unlock(&DILITHIUM_MUTEX); + } + return(hashstr); +} + +int32_t dilithium_Qmsghash(uint8_t *msg,CTransaction tx,int32_t numvouts,std::vector voutpubtxids) +{ + CScript data; uint256 hash; int32_t i,numvins,len = 0; std::vector vintxids; std::vector vinprevns; std::vector vouts; + numvins = tx.vin.size(); + for (i=0; i bigpub) +{ + CScript opret; uint8_t evalcode = EVAL_DILITHIUM; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << handle << pk << bigpub); + return(opret); +} + +uint8_t dilithium_registeropretdecode(std::string &handle,CPubKey &pk,std::vector &bigpub,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> handle; ss >> pk; ss >> bigpub) != 0 && e == EVAL_DILITHIUM && f == 'R' ) + { + return(f); + } + return(0); +} + +CScript dilithium_sendopret(uint256 destpubtxid) +{ + CScript opret; uint8_t evalcode = EVAL_DILITHIUM; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'x' << destpubtxid); + return(opret); +} + +uint8_t dilithium_sendopretdecode(uint256 &destpubtxid,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> destpubtxid) != 0 && e == EVAL_DILITHIUM && f == 'x' ) + { + return(f); + } + return(0); +} + +CScript dilithium_spendopret(uint256 destpubtxid,std::vector sig) +{ + CScript opret; uint8_t evalcode = EVAL_DILITHIUM; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'y' << destpubtxid << sig); + return(opret); +} + +uint8_t dilithium_spendopretdecode(uint256 &destpubtxid,std::vector &sig,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> destpubtxid; ss >> sig) != 0 && e == EVAL_DILITHIUM && f == 'y' ) + { + return(f); + } + return(0); +} + +CScript dilithium_Qsendopret(uint256 destpubtxid,std::vectorsig,std::vector voutpubtxids) +{ + CScript opret; uint8_t evalcode = EVAL_DILITHIUM; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'Q' << destpubtxid << sig << voutpubtxids); + return(opret); +} + +uint8_t dilithium_Qsendopretdecode(uint256 &destpubtxid,std::vector&sig,std::vector &voutpubtxids,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> destpubtxid; ss >> sig; ss >> voutpubtxids) != 0 && e == EVAL_DILITHIUM && f == 'Q' ) + { + return(f); + } + return(0); +} + +UniValue dilithium_rawtxresult(UniValue &result,std::string rawtx) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + //if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + // RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +char *dilithium_addr(char *coinaddr,uint8_t *buf,int32_t len) +{ + uint8_t rmd160[20],addrtype; + if ( len == CRYPTO_PUBLICKEYBYTES ) + addrtype = 55; + else if ( len == CRYPTO_SECRETKEYBYTES ) + addrtype = 63; + else + { + sprintf(coinaddr,"unexpected len.%d",len); + return(coinaddr); + } + calc_rmd160_sha256(rmd160,buf,len); + bitcoin_address(coinaddr,addrtype,rmd160,20); + return(coinaddr); +} + +char *dilithium_hexstr(char *str,uint8_t *buf,int32_t len) +{ + int32_t i; + for (i=0; i bigpub; + if ( myGetTransaction(pubtxid,tx,hashBlock) != 0 ) + { + if ( (numvouts= tx.vout.size()) > 1 ) + { + if ( (funcid= dilithium_registeropretdecode(handle,pk33,bigpub,tx.vout[numvouts-1].scriptPubKey)) == 'R' && bigpub.size() == CRYPTO_PUBLICKEYBYTES ) + { + memcpy(pk,&bigpub[0],CRYPTO_PUBLICKEYBYTES); + return(0); + } else return(-2); + } + } + return(-1); +} + +UniValue dilithium_keypair(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); uint8_t seed[SEEDBYTES],pk[CRYPTO_PUBLICKEYBYTES],sk[CRYPTO_SECRETKEYBYTES]; char coinaddr[64],str[CRYPTO_SECRETKEYBYTES*2+1]; int32_t i,n,externalflag=0; + Myprivkey(seed); + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + { + if ( cclib_parsehash(seed,jitem(params,0),32) < 0 ) + { + randombytes(seed,SEEDBYTES); + result.push_back(Pair("status","using random high entropy seed")); + result.push_back(Pair("seed",dilithium_hexstr(str,seed,SEEDBYTES))); + } + externalflag = 1; + } + _dilithium_keypair(pk,sk,seed); + result.push_back(Pair("pubkey",dilithium_hexstr(str,pk,CRYPTO_PUBLICKEYBYTES))); + result.push_back(Pair("privkey",dilithium_hexstr(str,sk,CRYPTO_SECRETKEYBYTES))); + result.push_back(Pair("pkaddr",dilithium_addr(coinaddr,pk,CRYPTO_PUBLICKEYBYTES))); + result.push_back(Pair("skaddr",dilithium_addr(coinaddr,sk,CRYPTO_SECRETKEYBYTES))); + if ( externalflag == 0 ) + result.push_back(Pair("warning","test mode using privkey for -pubkey, only for testing. there is no point using quantum secure signing if you are using a privkey with a known secp256k1 pubkey!!")); + result.push_back(Pair("result","success")); + return(result); +} + +CPubKey Faucet_pubkeyget() +{ + struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_FAUCET); + return(GetUnspendable(cp,0)); +} + +UniValue dilithium_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey faucetpk,mypk,dilithiumpk; uint8_t seed[SEEDBYTES],pk[CRYPTO_PUBLICKEYBYTES],sk[CRYPTO_SECRETKEYBYTES]; char coinaddr[64],str[CRYPTO_SECRETKEYBYTES*2+1]; int64_t CCchange,inputs; std::vector bigpub; int32_t i,n,warningflag = 0; + if ( txfee == 0 ) + txfee = DILITHIUM_TXFEE; + faucetpk = Faucet_pubkeyget(); + mypk = pubkey2pk(Mypubkey()); + dilithiumpk = GetUnspendable(cp,0); + if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 1 || n == 2) ) + { + std::string handle(jstr(jitem(params,0),0)); + result.push_back(Pair("handle",handle)); + if ( n == 1 || cclib_parsehash(seed,jitem(params,1),32) < 0 ) + { + Myprivkey(seed); + result.push_back(Pair("warning","test mode using privkey for -pubkey, only for testing. there is no point using quantum secure signing if you are using a privkey with a known secp256k1 pubkey!!")); + } + _dilithium_keypair(pk,sk,seed); + result.push_back(Pair("pkaddr",dilithium_addr(coinaddr,pk,CRYPTO_PUBLICKEYBYTES))); + result.push_back(Pair("skaddr",dilithium_addr(coinaddr,sk,CRYPTO_SECRETKEYBYTES))); + for (i=0; i 0 ) + { + if ( inputs > txfee ) + CCchange = (inputs - txfee); + else CCchange = 0; + if ( AddNormalinputs(mtx,mypk,COIN+3*txfee,64) >= 3*txfee ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,2*txfee,dilithiumpk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_FAUCET,COIN,faucetpk)); + if ( CCchange != 0 ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dilithiumpk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,dilithium_registeropret(handle,mypk,bigpub)); + return(musig_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"couldnt find enough funds")); + } else return(cclib_error(result,"not enough parameters")); + } else return(cclib_error(result,"not dilithiumpk funds")); +} + +UniValue dilithium_sign(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); uint8_t seed[SEEDBYTES],msg[32],rmd160[20],pk[CRYPTO_PUBLICKEYBYTES],sk[CRYPTO_SECRETKEYBYTES],sm[32+CRYPTO_BYTES]; char coinaddr[64],str[(32+CRYPTO_BYTES)*2+1]; int32_t n,smlen; + if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 1 || n == 2) ) + { + if ( cclib_parsehash(msg,jitem(params,0),32) < 0 ) + return(cclib_error(result,"couldnt parse message to sign")); + else if ( n == 1 || cclib_parsehash(seed,jitem(params,1),32) < 0 ) + { + Myprivkey(seed); + result.push_back(Pair("warning","test mode using privkey for -pubkey, only for testing. there is no point using quantum secure signing if you are using a privkey with a known secp256k1 pubkey!!")); + } + _dilithium_keypair(pk,sk,seed); + result.push_back(Pair("msg32",dilithium_hexstr(str,msg,32))); + result.push_back(Pair("pkaddr",dilithium_addr(coinaddr,pk,CRYPTO_PUBLICKEYBYTES))); + result.push_back(Pair("skaddr",dilithium_addr(coinaddr,sk,CRYPTO_SECRETKEYBYTES))); + _dilithium_sign(sm,&smlen,msg,32,sk); + if ( smlen == 32+CRYPTO_BYTES ) + { + result.push_back(Pair("signature",dilithium_hexstr(str,sm,smlen))); + calc_rmd160_sha256(rmd160,sm,smlen); + result.push_back(Pair("sighash",dilithium_hexstr(str,rmd160,20))); + return(result); + } else return(cclib_error(result,"unexpected signed message len")); + } else return(cclib_error(result,"not enough parameters")); +} + +UniValue dilithium_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); CPubKey pk33; uint8_t rmd160[20],msg[32],msg2[CRYPTO_BYTES+32],pk[CRYPTO_PUBLICKEYBYTES],sm[32+CRYPTO_BYTES]; uint256 pubtxid; char coinaddr[64],str[(32+CRYPTO_BYTES)*2+1]; int32_t smlen=32+CRYPTO_BYTES,mlen,n; std::string handle; + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 3 ) + { + pubtxid = juint256(jitem(params,0)); + if ( dilithium_bigpubget(handle,pk33,pk,pubtxid) < 0 ) + return(cclib_error(result,"couldnt parse message to sign")); + else if ( cclib_parsehash(msg,jitem(params,1),32) < 0 ) + return(cclib_error(result,"couldnt parse message to sign")); + else if ( cclib_parsehash(sm,jitem(params,2),smlen) < 0 ) + return(cclib_error(result,"couldnt parse sig")); + else + { + calc_rmd160_sha256(rmd160,sm,smlen); + result.push_back(Pair("sighash",dilithium_hexstr(str,rmd160,20))); + if ( _dilithium_verify(msg2,&mlen,sm,smlen,pk) < 0 ) + return(cclib_error(result,"dilithium verify error")); + else if ( mlen != 32 ) + return(cclib_error(result,"message len mismatch")); + else if ( memcmp(msg2,msg,32) != 0 ) + return(cclib_error(result,"message content mismatch")); + result.push_back(Pair("msg32",dilithium_hexstr(str,msg,32))); + result.push_back(Pair("handle",handle)); + result.push_back(Pair("pkaddr",dilithium_addr(coinaddr,pk,CRYPTO_PUBLICKEYBYTES))); + result.push_back(Pair("result","success")); + return(result); + } + } else return(cclib_error(result,"not enough parameters")); +} + +UniValue dilithium_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx,checkhandle; CPubKey destpub33,mypk,dilithiumpk; int32_t i,n; int64_t amount; uint256 destpubtxid; uint8_t pk[CRYPTO_PUBLICKEYBYTES]; + if ( txfee == 0 ) + txfee = DILITHIUM_TXFEE; + mypk = pubkey2pk(Mypubkey()); + dilithiumpk = GetUnspendable(cp,0); + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 3 ) + { + amount = jdouble(jitem(params,2),0)*COIN + 0.0000000049; + std::string handle(jstr(jitem(params,0),0)); + result.push_back(Pair("handle",handle)); + destpubtxid = juint256(jitem(params,1)); + if ( dilithium_bigpubget(checkhandle,destpub33,pk,destpubtxid) < 0 ) + return(cclib_error(result,"couldnt parse message to sign")); + else if ( handle == checkhandle ) + { + if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,destpub33)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,dilithium_sendopret(destpubtxid)); + return(musig_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"couldnt find enough funds")); + } else return(cclib_error(result,"handle mismatch")); + } else return(cclib_error(result,"not enough parameters")); +} + +UniValue dilithium_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey mypk,destpub33; CTransaction vintx; uint256 prevhash,hashBlock,destpubtxid; int32_t i,smlen,n,numvouts; char str[129],*scriptstr; CTxOut vout; std::string handle; uint8_t pk[CRYPTO_PUBLICKEYBYTES],pk2[CRYPTO_PUBLICKEYBYTES],sk[CRYPTO_SECRETKEYBYTES],msg[32],seed[32]; std::vector sig; + if ( txfee == 0 ) + txfee = DILITHIUM_TXFEE; + mypk = pubkey2pk(Mypubkey()); + if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 2 || n == 3) ) + { + prevhash = juint256(jitem(params,0)); + scriptstr = jstr(jitem(params,1),0); + if ( n == 2 || cclib_parsehash(seed,jitem(params,2),32) < 0 ) + { + Myprivkey(seed); + result.push_back(Pair("warning","test mode using privkey for -pubkey, only for testing. there is no point using quantum secure signing if you are using a privkey with a known secp256k1 pubkey!!")); + } + _dilithium_keypair(pk,sk,seed); + if ( is_hexstr(scriptstr,0) != 0 ) + { + CScript scriptPubKey; + scriptPubKey.resize(strlen(scriptstr)/2); + decode_hex(&scriptPubKey[0],strlen(scriptstr)/2,scriptstr); + if ( myGetTransaction(prevhash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + vout.nValue = vintx.vout[0].nValue - txfee; + vout.scriptPubKey = scriptPubKey; + musig_prevoutmsg(msg,prevhash,vout.scriptPubKey); + sig.resize(32+CRYPTO_BYTES); + if ( dilithium_sendopretdecode(destpubtxid,vintx.vout[numvouts-1].scriptPubKey) == 'x' ) + { + if ( dilithium_bigpubget(handle,destpub33,pk2,destpubtxid) < 0 ) + return(cclib_error(result,"couldnt get bigpub")); + else if ( memcmp(pk,pk2,sizeof(pk)) != 0 ) + return(cclib_error(result,"dilithium bigpub mismatch")); + else if ( destpub33 != mypk ) + return(cclib_error(result,"destpub33 is not for this -pubkey")); + else if ( _dilithium_sign(&sig[0],&smlen,msg,32,sk) < 0 ) + return(cclib_error(result,"dilithium signing error")); + else if ( smlen != 32+CRYPTO_BYTES ) + return(cclib_error(result,"siglen error")); + mtx.vin.push_back(CTxIn(prevhash,0)); + mtx.vout.push_back(vout); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,dilithium_spendopret(destpubtxid,sig)); + return(dilithium_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"couldnt decode send opret")); + } else return(cclib_error(result,"couldnt find vin0")); + } else return(cclib_error(result,"script or bad destpubtxid is not hex")); + } else return(cclib_error(result,"need to have exactly 2 params sendtxid, scriptPubKey")); +} + +int64_t dilithium_inputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 destpubtxid,int64_t total,int32_t maxinputs,char *cmpaddr) +{ + char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 checktxid,txid,hashBlock; std::vector origpubkey,tmpsig; CTransaction vintx; int32_t vout,numvouts,n = 0; std::vector voutpubtxids; + std::vector > unspentOutputs; + GetCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr,true); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f vs %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN,(double)threshold/COIN); + if ( it->second.satoshis < threshold || it->second.satoshis == DILITHIUM_TXFEE ) + continue; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + if ( (nValue= IsCClibvout(cp,vintx,vout,cmpaddr)) > DILITHIUM_TXFEE && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + if ( (dilithium_Qsendopretdecode(checktxid,tmpsig,voutpubtxids,vintx.vout[numvouts-1].scriptPubKey) == 'Q' && vout < voutpubtxids.size() && destpubtxid == voutpubtxids[vout]) || (dilithium_sendopretdecode(checktxid,vintx.vout[numvouts-1].scriptPubKey) == 'x' && destpubtxid == checktxid) ) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } + } //else fprintf(stderr,"nValue %.8f too small or already spent in mempool\n",(double)nValue/COIN); + } else fprintf(stderr,"couldnt get tx\n"); + } + return(totalinputs); +} + +UniValue dilithium_Qsend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey mypk,destpub33; CTransaction tx,vintx; uint256 prevhash,mypubtxid,hashBlock,destpubtxid; int64_t amount,inputsum,outputsum,change; int32_t i,smlen,n,numvouts; char str[129],myCCaddr[64],*scriptstr; CTxOut vout; std::string handle; uint8_t pk[CRYPTO_PUBLICKEYBYTES],pk2[CRYPTO_PUBLICKEYBYTES],sk[CRYPTO_SECRETKEYBYTES],msg[32],seed[32]; std::vector sig; std::vector voutpubtxids; + if ( txfee == 0 ) + txfee = DILITHIUM_TXFEE; + mypk = pubkey2pk(Mypubkey()); + GetCCaddress(cp,myCCaddr,mypk); + if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 4 && (n & 1) == 0 ) + { + mypubtxid = juint256(jitem(params,0)); + if ( cclib_parsehash(seed,jitem(params,1),32) < 0 ) + { + Myprivkey(seed); + result.push_back(Pair("warning","test mode using privkey for -pubkey, only for testing. there is no point using quantum secure signing if you are using a privkey with a known secp256k1 pubkey!!")); + } + _dilithium_keypair(pk,sk,seed); + outputsum = 0; + for (i=2; ievalcode,amount,destpub33)); + voutpubtxids.push_back(prevhash); // binds destpub22 CC addr with dilithium bigpub + } + } + else + { + CScript scriptPubKey; + scriptPubKey.resize(strlen(scriptstr)/2); + decode_hex(&scriptPubKey[0],strlen(scriptstr)/2,scriptstr); + vout.nValue = amount; + vout.scriptPubKey = scriptPubKey; + mtx.vout.push_back(vout); + voutpubtxids.push_back(zeroid); + } + outputsum += amount; + } + if ( (inputsum= dilithium_inputs(cp,mtx,mypk,mypubtxid,outputsum+txfee,64,myCCaddr)) >= outputsum+txfee ) + { + change = (inputsum - outputsum - txfee); + if ( change >= txfee ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,mypk)); + voutpubtxids.push_back(mypubtxid); + } + tx = mtx; + dilithium_Qmsghash(msg,tx,(int32_t)voutpubtxids.size(),voutpubtxids); + //for (i=0; i<32; i++) + // fprintf(stderr,"%02x",msg[i]); + //fprintf(stderr," msg\n"); + sig.resize(32+CRYPTO_BYTES); + if ( dilithium_bigpubget(handle,destpub33,pk2,mypubtxid) < 0 ) + return(cclib_error(result,"couldnt get bigpub")); + else if ( memcmp(pk,pk2,sizeof(pk)) != 0 ) + return(cclib_error(result,"dilithium bigpub mismatch")); + else if ( destpub33 != mypk ) + return(cclib_error(result,"destpub33 is not for this -pubkey")); + else if ( _dilithium_sign(&sig[0],&smlen,msg,32,sk) < 0 ) + return(cclib_error(result,"dilithium signing error")); + else if ( smlen != 32+CRYPTO_BYTES ) + return(cclib_error(result,"siglen error")); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,dilithium_Qsendopret(mypubtxid,sig,voutpubtxids)); + return(dilithium_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"Q couldnt find enough Q or x inputs")); + } else return(cclib_error(result,"need to have exactly 2 params sendtxid, scriptPubKey")); +} + +bool dilithium_Qvalidate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + int32_t i,numvins,numvouts,mlen,smlen=CRYPTO_BYTES+32; CPubKey destpub33; std::string handle; uint256 tmptxid,hashBlock,destpubtxid,signerpubtxid; CTransaction vintx; std::vector tmpsig,sig,vopret; uint8_t msg[32],msg2[CRYPTO_BYTES+32],pk[CRYPTO_PUBLICKEYBYTES],*script; std::vector voutpubtxids; + numvins = tx.vin.size(); + signerpubtxid = zeroid; + for (i=0; i 1 ) + { + GetOpReturnData(vintx.vout[numvouts-1].scriptPubKey,vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'Q' ) + { + if ( dilithium_Qsendopretdecode(tmptxid,tmpsig,voutpubtxids,vintx.vout[numvouts-1].scriptPubKey) != 'Q' ) + return eval->Invalid("couldnt decode destpubtxid from Qsend"); + else if ( tx.vin[i].prevout.n > voutpubtxids.size() ) + return eval->Invalid("no destpubtxid for prevout.n"); + destpubtxid = voutpubtxids[tx.vin[i].prevout.n]; + } + else + { + if ( dilithium_sendopretdecode(destpubtxid,vintx.vout[numvouts-1].scriptPubKey) != 'x' ) + return eval->Invalid("couldnt decode destpubtxid from send"); + } + if ( signerpubtxid == zeroid ) + signerpubtxid = destpubtxid; + else if ( destpubtxid != signerpubtxid ) + return eval->Invalid("destpubtxid of vini doesnt match first one"); + } + } + } + if ( signerpubtxid != zeroid ) + { + numvouts = tx.vout.size(); + if ( dilithium_Qsendopretdecode(destpubtxid,sig,voutpubtxids,tx.vout[numvouts-1].scriptPubKey) == 'Q' && destpubtxid == signerpubtxid && sig.size() == smlen ) + { + if ( dilithium_Qmsghash(msg,tx,numvouts-1,voutpubtxids) < 0 ) + return eval->Invalid("couldnt get Qmsghash"); + else if ( dilithium_bigpubget(handle,destpub33,pk,signerpubtxid) < 0 ) + return eval->Invalid("couldnt get bigpub"); + else + { + if ( _dilithium_verify(msg2,&mlen,&sig[0],smlen,pk) < 0 ) + return eval->Invalid("failed dilithium verify"); + else if ( mlen != 32 || memcmp(msg,msg2,32) != 0 ) + { + for (i=0; i<32; i++) + fprintf(stderr,"%02x",msg[i]); + fprintf(stderr," vs "); + for (i=0; iInvalid("failed dilithium msg verify"); + } + else return true; + } + } else return eval->Invalid("failed decode Qsend"); + } else return eval->Invalid("unexpected zero signerpubtxid"); +} + +int32_t dilithium_registrationpub33(char *pkaddr,CPubKey &pub33,uint256 txid) +{ + std::string handle; std::vector bigpub; CTransaction tx; uint256 hashBlock; int32_t numvouts; + pkaddr[0] = 0; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( dilithium_registeropretdecode(handle,pub33,bigpub,tx.vout[numvouts-1].scriptPubKey) == 'R' ) + { + dilithium_addr(pkaddr,&bigpub[0],CRYPTO_PUBLICKEYBYTES); + return(0); + } + } + return(-1); +} + +void dilithium_handleinit(struct CCcontract_info *cp) +{ + static int32_t didinit; + std::vector > txids; struct dilithium_handle *hashstr; CPubKey dilithiumpk,pub33; uint256 txid,hashBlock; CTransaction txi; int32_t numvouts; std::vector bigpub; std::string handle; char CCaddr[64]; + if ( didinit != 0 ) + return; + pthread_mutex_init(&DILITHIUM_MUTEX,NULL); + dilithiumpk = GetUnspendable(cp,0); + GetCCaddress(cp,CCaddr,dilithiumpk); + SetCCtxids(txids,CCaddr,true); + for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) + { + txid = it->first.txhash; + if ( myGetTransaction(txid,txi,hashBlock) != 0 && (numvouts= txi.vout.size()) > 1 ) + { + if ( dilithium_registeropretdecode(handle,pub33,bigpub,txi.vout[numvouts-1].scriptPubKey) == 'R' ) + { + if ( (hashstr= dilithium_handlenew((char *)handle.c_str())) != 0 ) + { + if ( hashstr->destpubtxid != txid ) + { + if ( hashstr->destpubtxid != zeroid ) + fprintf(stderr,"overwriting %s %s with %s\n",handle.c_str(),hashstr->destpubtxid.GetHex().c_str(),txid.GetHex().c_str()); + fprintf(stderr,"%s <- %s\n",handle.c_str(),txid.GetHex().c_str()); + hashstr->destpubtxid = txid; + } + } + } + } + } + didinit = 1; +} + +UniValue dilithium_handleinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); CPubKey pub33; int32_t i,n; char *handlestr,pkaddr[64],str[67]; struct dilithium_handle *hashstr; + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + { + dilithium_handleinit(cp); + if ( (handlestr= jstr(jitem(params,0),0)) != 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("handle",handlestr)); + if ( (hashstr= dilithium_handlefind(handlestr)) != 0 ) + { + result.push_back(Pair("destpubtxid",hashstr->destpubtxid.GetHex().c_str())); + if ( dilithium_registrationpub33(pkaddr,pub33,hashstr->destpubtxid) == 0 ) + { + for (i=0; i<33; i++) + sprintf(&str[i<<1],"%02x",((uint8_t *)pub33.begin())[i]); + str[i<<1] = 0; + result.push_back(Pair("pkaddr",pkaddr)); + } + result.push_back(Pair("pubkey",str)); + } else result.push_back(Pair("status","available")); + return(result); + } + } + result.push_back(Pair("result","error")); + return(result); +} + +bool dilithium_Rvalidate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + static int32_t didinit; + uint256 txid; int32_t numvouts; struct dilithium_handle *hashstr; std::string handle; std::vector bigpub; CPubKey oldpub33,pub33,dilithiumpk; CTxOut vout,vout0; char pkaddr[64]; + if ( height < 14500 ) + return(true); + dilithium_handleinit(cp); + dilithiumpk = GetUnspendable(cp,0); + if ( (numvouts= tx.vout.size()) <= 1 ) + return eval->Invalid("not enough vouts for registration tx"); + else if ( dilithium_registeropretdecode(handle,pub33,bigpub,tx.vout[numvouts-1].scriptPubKey) == 'R' ) + { + // relies on all current block tx to be put into mempool + txid = tx.GetHash(); + vout0 = MakeCC1vout(cp->evalcode,2*DILITHIUM_TXFEE,dilithiumpk); + vout = MakeCC1vout(EVAL_FAUCET,COIN,Faucet_pubkeyget()); + if ( tx.vout[0] != vout0 ) + return eval->Invalid("mismatched vout0 for register"); + else if ( tx.vout[1].nValue != DILITHIUM_TXFEE ) + return eval->Invalid("vout1 for register not txfee"); + else if ( tx.vout[2] != vout ) + return eval->Invalid("register not sending to faucet"); + else if ( (hashstr= dilithium_handlenew((char *)handle.c_str())) == 0 ) + return eval->Invalid("error creating dilithium handle"); + else if ( hashstr->destpubtxid == zeroid ) + { + hashstr->destpubtxid = txid; + return(true); + } + else + { + if ( hashstr->destpubtxid == txid ) + return(true); + else if ( dilithium_registrationpub33(pkaddr,oldpub33,hashstr->destpubtxid) == 0 ) + { + if ( oldpub33 == pub33 ) + { + hashstr->destpubtxid = txid; + fprintf(stderr,"ht.%d %s <- %s\n",height,handle.c_str(),txid.GetHex().c_str()); + return(true); + } + } + return eval->Invalid("duplicate dilithium handle rejected"); + } + } else return eval->Invalid("couldnt decode register opret"); +} + +bool dilithium_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + CPubKey destpub33; std::string handle; uint256 hashBlock,destpubtxid,checktxid; CTransaction vintx; int32_t numvouts,mlen,smlen=CRYPTO_BYTES+32; std::vector sig,vopret; uint8_t msg[32],msg2[CRYPTO_BYTES+32],pk[CRYPTO_PUBLICKEYBYTES],*script; + // if all dilithium tx -> do multispend/send, else: + numvouts = tx.vout.size(); + GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'R' ) + return(dilithium_Rvalidate(cp,height,eval,tx)); + else if ( script[1] == 'Q' ) + return(dilithium_Qvalidate(cp,height,eval,tx)); + else if ( tx.vout.size() != 2 ) + return eval->Invalid("numvouts != 2"); + else if ( tx.vin.size() != 1 ) + return eval->Invalid("numvins != 1"); + else if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) + return eval->Invalid("illegal normal vin0"); + else if ( myGetTransaction(tx.vin[0].prevout.hash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + if ( dilithium_sendopretdecode(destpubtxid,vintx.vout[numvouts-1].scriptPubKey) == 'x' ) + { + if ( dilithium_spendopretdecode(checktxid,sig,tx.vout[tx.vout.size()-1].scriptPubKey) == 'y' ) + { + if ( destpubtxid == checktxid && sig.size() == smlen ) + { + musig_prevoutmsg(msg,tx.vin[0].prevout.hash,tx.vout[0].scriptPubKey); + if ( dilithium_bigpubget(handle,destpub33,pk,destpubtxid) < 0 ) + return eval->Invalid("couldnt get bigpub"); + else + { + if ( _dilithium_verify(msg2,&mlen,&sig[0],smlen,pk) < 0 ) + return eval->Invalid("failed dilithium verify"); + else if ( mlen != 32 || memcmp(msg,msg2,32) != 0 ) + return eval->Invalid("failed dilithium msg verify"); + else return(true); + } + } else return eval->Invalid("destpubtxid or sig size didnt match send opret"); + } else return eval->Invalid("failed decode dilithium spendopret"); + } else return eval->Invalid("couldnt decode send opret"); + } else return eval->Invalid("couldnt find vin0 tx"); +} + diff --git a/src/cc/dilithium.h b/src/cc/dilithium.h new file mode 100644 index 000000000..e16010a45 --- /dev/null +++ b/src/cc/dilithium.h @@ -0,0 +1,475 @@ +#include + +/* +#ifndef CPUCYCLES_H +#define CPUCYCLES_H + +#ifdef DBENCH +#define DBENCH_START() uint64_t time = cpucycles_start() +#define DBENCH_STOP(t) t += cpucycles_stop() - time - timing_overhead +#else +#define DBENCH_START() +#define DBENCH_STOP(t) +#endif + +#ifdef USE_RDPMC // Needs echo 2 > /sys/devices/cpu/rdpmc +#ifdef SERIALIZE_RDC + +static inline uint64_t cpucycles_start(void) { + const uint32_t ecx = (1U << 30) + 1; + uint64_t result; + + asm volatile("cpuid; movl %1,%%ecx; rdpmc; shlq $32,%%rdx; orq %%rdx,%%rax" + : "=&a" (result) : "r" (ecx) : "rbx", "rcx", "rdx"); + + return result; +} + +static inline uint64_t cpucycles_stop(void) { + const uint32_t ecx = (1U << 30) + 1; + uint64_t result, dummy; + + asm volatile("rdpmc; shlq $32,%%rdx; orq %%rdx,%%rax; movq %%rax,%0; cpuid" + : "=&r" (result), "=c" (dummy) : "c" (ecx) : "rax", "rbx", "rdx"); + + return result; +} + +#else + +static inline uint64_t cpucycles_start(void) { + const uint32_t ecx = (1U << 30) + 1; + uint64_t result; + + asm volatile("rdpmc; shlq $32,%%rdx; orq %%rdx,%%rax" + : "=a" (result) : "c" (ecx) : "rdx"); + + return result; +} + +static inline uint64_t cpucycles_stop(void) { + const uint32_t ecx = (1U << 30) + 1; + uint64_t result; + + asm volatile("rdpmc; shlq $32,%%rdx; orq %%rdx,%%rax" + : "=a" (result) : "c" (ecx) : "rdx"); + + return result; +} + +#endif +#else +#ifdef SERIALIZE_RDC + +static inline uint64_t cpucycles_start(void) { + uint64_t result; + + asm volatile("cpuid; rdtsc; shlq $32,%%rdx; orq %%rdx,%%rax" + : "=a" (result) : : "%rbx", "%rcx", "%rdx"); + + return result; +} + +static inline uint64_t cpucycles_stop(void) { + uint64_t result; + + asm volatile("rdtscp; shlq $32,%%rdx; orq %%rdx,%%rax; mov %%rax,%0; cpuid" + : "=r" (result) : : "%rax", "%rbx", "%rcx", "%rdx"); + + return result; +} + +#else + +static inline uint64_t cpucycles_start(void) { + uint64_t result; + + asm volatile("rdtsc; shlq $32,%%rdx; orq %%rdx,%%rax" + : "=a" (result) : : "%rdx"); + + return result; +} + +static inline uint64_t cpucycles_stop(void) { + uint64_t result; + + asm volatile("rdtsc; shlq $32,%%rdx; orq %%rdx,%%rax" + : "=a" (result) : : "%rdx"); + + return result; +} + +#endif +#endif + +int64_t cpucycles_overhead(void); + +#endif*/ + +#ifndef FIPS202_H +#define FIPS202_H + + +#define SHAKE128_RATE 168 +#define SHAKE256_RATE 136 + +void shake128_absorb(uint64_t *s, + const uint8_t *input, + int32_t inlen); + +void shake128_squeezeblocks(uint8_t *output, + int32_t nblocks, + uint64_t *s); + +void shake256_absorb(uint64_t *s, + const uint8_t *input, + int32_t inlen); + +void shake256_squeezeblocks(uint8_t *output, + int32_t nblocks, + uint64_t *s); + +void shake128(uint8_t *output, + int32_t outlen, + const uint8_t *input, + int32_t inlen); + +void shake256(uint8_t *output, + int32_t outlen, + const uint8_t *input, + int32_t inlen); + +#endif + +#ifndef PARAMS_H +#define PARAMS_H + +#ifndef MODE +#define MODE 3 +#endif + +#define SEEDBYTES 32U +#define CRHBYTES 48U +#define N 256U +#define Q 8380417U +#define QBITS 23U +#define ROOT_OF_UNITY 1753U +#define D 14U +#define GAMMA1 ((Q - 1U)/16U) +#define GAMMA2 (GAMMA1/2U) +#define ALPHA (2U*GAMMA2) + +#if MODE == 0 +#define K 3U +#define L 2U +#define ETA 7U +#define SETABITS 4U +#define BETA 375U +#define OMEGA 64U + +#elif MODE == 1 +#define K 4U +#define L 3U +#define ETA 6U +#define SETABITS 4U +#define BETA 325U +#define OMEGA 80U + +#elif MODE == 2 +#define K 5U +#define L 4U +#define ETA 5U +#define SETABITS 4U +#define BETA 275U +#define OMEGA 96U + +#elif MODE == 3 +#define K 6U +#define L 5U +#define ETA 3U +#define SETABITS 3U +#define BETA 175U +#define OMEGA 120U + +#endif + +#define POL_SIZE_PACKED ((N*QBITS)/8) +#define POLT1_SIZE_PACKED ((N*(QBITS - D))/8) +#define POLT0_SIZE_PACKED ((N*D)/8) +#define POLETA_SIZE_PACKED ((N*SETABITS)/8) +#define POLZ_SIZE_PACKED ((N*(QBITS - 3))/8) +#define POLW1_SIZE_PACKED ((N*4)/8) +#define POLVECK_SIZE_PACKED (K*POL_SIZE_PACKED) +#define POLVECL_SIZE_PACKED (L*POL_SIZE_PACKED) + +#define CRYPTO_PUBLICKEYBYTES (SEEDBYTES + K*POLT1_SIZE_PACKED) +#define CRYPTO_SECRETKEYBYTES (2*SEEDBYTES + (L + K)*POLETA_SIZE_PACKED + CRHBYTES + K*POLT0_SIZE_PACKED) +#define CRYPTO_BYTES (L*POLZ_SIZE_PACKED + (OMEGA + K) + (N/8 + 8)) + +#endif +#ifndef POLY_H +#define POLY_H + +//#include +//#include "params.h" +//#include "fips202.h" + +typedef struct { + uint32_t coeffs[N]; +} poly __attribute__((aligned(32))); + +void poly_reduce(poly *a); +void poly_csubq(poly *a); +void poly_freeze(poly *a); + +void poly_add(poly *c, const poly *a, const poly *b); +void poly_sub(poly *c, const poly *a, const poly *b); +void poly_neg(poly *a); +void poly_shiftl(poly *a, uint32_t k); + +void poly_ntt(poly *a); +void poly_invntt_montgomery(poly *a); +void poly_pointwise_invmontgomery(poly *c, const poly *a, const poly *b); + +void poly_power2round(poly *a1, poly *a0, const poly *a); +void poly_decompose(poly *a1, poly *a0, const poly *a); +uint32_t poly_make_hint(poly *h, const poly *a, const poly *b); +void poly_use_hint(poly *a, const poly *b, const poly *h); + +int poly_chknorm(const poly *a, uint32_t B); +void poly_uniform(poly *a, const uint8_t *buf); +void poly_uniform_eta(poly *a, + const uint8_t seed[SEEDBYTES], + uint8_t nonce); +void poly_uniform_gamma1m1(poly *a, + const uint8_t seed[SEEDBYTES + CRHBYTES], + uint16_t nonce); + +void polyeta_pack(uint8_t *r, const poly *a); +void polyeta_unpack(poly *r, const uint8_t *a); + +void polyt1_pack(uint8_t *r, const poly *a); +void polyt1_unpack(poly *r, const uint8_t *a); + +void polyt0_pack(uint8_t *r, const poly *a); +void polyt0_unpack(poly *r, const uint8_t *a); + +void polyz_pack(uint8_t *r, const poly *a); +void polyz_unpack(poly *r, const uint8_t *a); + +void polyw1_pack(uint8_t *r, const poly *a); +#endif +#ifndef POLYVEC_H +#define POLYVEC_H + +//#include +//#include "params.h" +//#include "poly.h" + +/* Vectors of polynomials of length L */ +typedef struct { + poly vec[L]; +} polyvecl; + +void polyvecl_freeze(polyvecl *v); + +void polyvecl_add(polyvecl *w, const polyvecl *u, const polyvecl *v); + +void polyvecl_ntt(polyvecl *v); +void polyvecl_pointwise_acc_invmontgomery(poly *w, + const polyvecl *u, + const polyvecl *v); + +int polyvecl_chknorm(const polyvecl *v, uint32_t B); + + + +/* Vectors of polynomials of length K */ +typedef struct { + poly vec[K]; +} polyveck; + +void polyveck_reduce(polyveck *v); +void polyveck_csubq(polyveck *v); +void polyveck_freeze(polyveck *v); + +void polyveck_add(polyveck *w, const polyveck *u, const polyveck *v); +void polyveck_sub(polyveck *w, const polyveck *u, const polyveck *v); +void polyveck_shiftl(polyveck *v, uint32_t k); + +void polyveck_ntt(polyveck *v); +void polyveck_invntt_montgomery(polyveck *v); + +int polyveck_chknorm(const polyveck *v, uint32_t B); + +void polyveck_power2round(polyveck *v1, polyveck *v0, const polyveck *v); +void polyveck_decompose(polyveck *v1, polyveck *v0, const polyveck *v); +uint32_t polyveck_make_hint(polyveck *h, + const polyveck *u, + const polyveck *v); +void polyveck_use_hint(polyveck *w, const polyveck *v, const polyveck *h); + +#endif + +#ifndef NTT_H +#define NTT_H + +//#include +//#include "params.h" + +void ntt(uint32_t p[N]); +void invntt_frominvmont(uint32_t p[N]); + +#endif +#ifndef PACKING_H +#define PACKING_H + +//#include "params.h" +//#include "polyvec.h" + +void pack_pk(uint8_t pk[CRYPTO_PUBLICKEYBYTES], + const uint8_t rho[SEEDBYTES], const polyveck *t1); +void pack_sk(uint8_t sk[CRYPTO_SECRETKEYBYTES], + const uint8_t rho[SEEDBYTES], + const uint8_t key[SEEDBYTES], + const uint8_t tr[CRHBYTES], + const polyvecl *s1, + const polyveck *s2, + const polyveck *t0); +void pack_sig(uint8_t sig[CRYPTO_BYTES], + const polyvecl *z, const polyveck *h, const poly *c); + +void unpack_pk(uint8_t rho[SEEDBYTES], polyveck *t1, + const uint8_t pk[CRYPTO_PUBLICKEYBYTES]); +void unpack_sk(uint8_t rho[SEEDBYTES], + uint8_t key[SEEDBYTES], + uint8_t tr[CRHBYTES], + polyvecl *s1, + polyveck *s2, + polyveck *t0, + const uint8_t sk[CRYPTO_SECRETKEYBYTES]); +int unpack_sig(polyvecl *z, polyveck *h, poly *c, + const uint8_t sig[CRYPTO_BYTES]); + +#endif +#ifndef REDUCE_H +#define REDUCE_H + +//#include + +#define MONT 4193792U // 2^32 % Q +#define QINV 4236238847U // -q^(-1) mod 2^32 + +/* a <= Q*2^32 => r < 2*Q */ +uint32_t montgomery_reduce(uint64_t a); + +/* r < 2*Q */ +uint32_t reduce32(uint32_t a); + +/* a < 2*Q => r < Q */ +uint32_t csubq(uint32_t a); + +/* r < Q */ +uint32_t freeze(uint32_t a); + +#endif +#ifndef ROUNDING_H +#define ROUNDING_H + +//#include + +uint32_t power2round(const uint32_t a, uint32_t *a0); +uint32_t decompose(uint32_t a, uint32_t *a0); +uint32_t make_hint(const uint32_t a, const uint32_t b); +uint32_t use_hint(const uint32_t a, const uint32_t hint); + +#endif +#ifndef SIGN_H +#define SIGN_H + +//#include "params.h" +//#include "poly.h" +//#include "polyvec.h" + +void expand_mat(polyvecl mat[K], const uint8_t rho[SEEDBYTES]); +void challenge(poly *c, const uint8_t mu[CRHBYTES], + const polyveck *w1); + +int crypto_sign_keypair(uint8_t *pk, uint8_t *sk); + +int crypto_sign(uint8_t *sm, int32_t *smlen, + const uint8_t *msg, int32_t len, + const uint8_t *sk); + +int crypto_sign_open(uint8_t *m, int32_t *mlen, + const uint8_t *sm, int32_t smlen, + const uint8_t *pk); + +#endif + +#ifndef API_H +#define API_H + +#ifndef MODE +#define MODE 3 +#endif + +#if MODE == 0 +#if CRYPTO_PUBLICKEYBYTES -896U +CRYPTO_PUBLICKEYBYTES size error +#endif +#if CRYPTO_SECRETKEYBYTES -2096U +CRYPTO_SECRETKEYBYTES size error +#endif +#if CRYPTO_BYTES -1387U +CRYPTO_BYTES size error +#endif + +#elif MODE == 1 +#if CRYPTO_PUBLICKEYBYTES -1184U +CRYPTO_PUBLICKEYBYTES size error +#endif +#if CRYPTO_SECRETKEYBYTES -2800U +CRYPTO_SECRETKEYBYTES size error +#endif +#if CRYPTO_BYTES -2044U +CRYPTO_BYTES size error +#endif + +#elif MODE == 2 +#if CRYPTO_PUBLICKEYBYTES -1472U +CRYPTO_PUBLICKEYBYTES size error +#endif +#if CRYPTO_SECRETKEYBYTES -3504U +CRYPTO_SECRETKEYBYTES size error +#endif +#if CRYPTO_BYTES -2701U +CRYPTO_BYTES size error +#endif + +#elif MODE == 3 +#if CRYPTO_PUBLICKEYBYTES -1760U +CRYPTO_PUBLICKEYBYTES size error +#endif +#if CRYPTO_SECRETKEYBYTES -3856U +CRYPTO_SECRETKEYBYTES size error +#endif +#if CRYPTO_BYTES -3366U +CRYPTO_BYTES size error +#endif + +#endif + +#define CRYPTO_ALGNAME "Dilithium" + +int crypto_sign_keypair(uint8_t *pk, uint8_t *sk); + +int crypto_sign(uint8_t *sm, int32_t *smlen, + const uint8_t *msg, int32_t len, + const uint8_t *sk); + +int crypto_sign_open(uint8_t *m, int32_t *mlen, + const uint8_t *sm, int32_t smlen, + const uint8_t *pk); + +#endif diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index ca2703bff..62f020063 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -85,6 +85,7 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) CCinit(cp,ecode); cp->didinit = 1; } + switch ( ecode ) { case EVAL_IMPORTPAYOUT: @@ -153,46 +154,17 @@ int32_t Eval::GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t time return komodo_notaries(pubkeys, height, timestamp); } - bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const { if (tx.vin.size() < 11) return false; - uint8_t seenNotaries[64] = {0}; - uint8_t notaries[64][33]; - int nNotaries = GetNotaries(notaries, height, timestamp); + CrosschainAuthority auth; + auth.requiredSigs = 11; + auth.size = GetNotaries(auth.notaries, height, timestamp); - BOOST_FOREACH(const CTxIn &txIn, tx.vin) - { - // Get notary pubkey - CTransaction tx; - uint256 hashBlock; - if (!GetTxUnconfirmed(txIn.prevout.hash, tx, hashBlock)) return false; - if (tx.vout.size() < txIn.prevout.n) return false; - CScript spk = tx.vout[txIn.prevout.n].scriptPubKey; - if (spk.size() != 35) return false; - std::vector scriptVec = std::vector(spk.begin(),spk.end()); - const unsigned char *pk = scriptVec.data(); - if (pk++[0] != 33) return false; - if (pk[33] != OP_CHECKSIG) return false; - - // Check it's a notary - for (int i=0; iInvalid("invalid faucetget txid"); Getscriptaddress(destaddr,tx.vout[i].scriptPubKey); - SetCCtxids(txids,destaddr); + SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition()); for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) { //int height = it->first.blockHeight; if ( CCduration(numblocks,it->first.txhash) > 0 && numblocks > 3 ) { - //fprintf(stderr,"would return error %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks); return eval->Invalid("faucet is only for brand new addresses"); } + //fprintf(stderr,"txid %s numblocks.%d ago\n",uint256_str(str,it->first.txhash),numblocks); } retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) @@ -145,8 +145,12 @@ int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPub char coinaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + SetCCunspents(unspentOutputs,coinaddr,true); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -157,7 +161,7 @@ int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPub // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( (nValue= IsFaucetvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( (nValue= IsFaucetvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); diff --git a/src/cc/fsm.cpp b/src/cc/fsm.cpp index 0bcc61a1b..e44f85e47 100644 --- a/src/cc/fsm.cpp +++ b/src/cc/fsm.cpp @@ -126,7 +126,7 @@ int64_t AddFSMInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/games/prices.c b/src/cc/games/prices.c new file mode 100644 index 000000000..1bc62533e --- /dev/null +++ b/src/cc/games/prices.c @@ -0,0 +1,289 @@ + +#include "prices.h" +#include +#include +#ifdef BUILD_GAMESCC +#include "../rogue/cursesd.h" +#else +#include +#endif + +#define SATOSHIDEN ((uint64_t)100000000L) +#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"prices",cmdstr,0,0,0) +extern int64_t Net_change,Betsize; + +int random_tetromino(struct games_state *rs) +{ + rs->seed = _games_rngnext(rs->seed); + return(rs->seed % NUM_TETROMINOS); +} + +int32_t pricesdata(struct games_player *P,void *ptr) +{ + tetris_game *tg = (tetris_game *)ptr; + P->gold = tg->points; + P->dungeonlevel = tg->level; + //fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); + return(0); +} + +void sleep_milli(int milliseconds) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = milliseconds * 1000 * 1000; + nanosleep(&ts, NULL); +} + +struct games_state globalR; +extern char Gametxidstr[]; +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); +uint64_t get_btcusd(); +int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize); + +void *gamesiterate(struct games_state *rs) +{ + bool running = true; uint32_t eventid = 0; int64_t price; + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + { + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen + timeout(0); + } + while ( running != 0 ) + { + //running = tg_tick(rs,tg,move); + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + { + } + if ( rs->guiflag != 0 ) + { +#ifdef STANDALONE + price = get_btcusd(); + //fprintf(stderr,"%llu -> t%u %.4f\n",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); + //issue_games_events(rs,Gametxidstr,eventid,price); + issue_bet(rs,price,Betsize); + eventid++; + doupdate(); + sleep(10); + switch ( getch() ) + { + case '+': Net_change++; break; + case '-': Net_change--; break; + case '0': Net_change = 0; break; + case '$': Betsize = SATOSHIDEN; break; + case '^': Betsize += (Betsize >> 3); break; + case '/': Betsize -= (Betsize >> 3); break; + } + /*if ( (counter++ % 10) == 0 ) + doupdate(); + c = games_readevent(rs); + if ( c <= 0x7f || skipcount == 0x3fff ) + { + if ( skipcount > 0 ) + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); + if ( c <= 0x7f ) + issue_games_events(rs,Gametxidstr,eventid,c); + if ( tg->level != prevlevel ) + { + flushkeystrokes(rs,0); + prevlevel = tg->level; + } + skipcount = 0; + } else skipcount++;*/ +#endif + } + else + { + if ( rs->replaydone != 0 ) + break; + if ( rs->sleeptime != 0 ) + { + sleep_milli(1); + } + /*if ( skipcount == 0 ) + { + c = games_readevent(rs); + //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); + if ( (c & 0x4000) == 0x4000 ) + { + skipcount = (c & 0x3fff); + c = 'S'; + } + } + if ( skipcount > 0 ) + skipcount--;*/ + } + eventid++; + } + return(0); +} + +#ifdef STANDALONE +#include +#include "dapps/dappstd.c" +int64_t Net_change,Betsize = SATOSHIDEN; + +char *send_curl(char *url,char *fname) +{ + char *retstr; + retstr = issue_curl(url); + return(retstr); +} + +cJSON *get_urljson(char *url,char *fname) +{ + char *jsonstr; cJSON *json = 0; + if ( (jsonstr= send_curl(url,fname)) != 0 ) + { + //printf("(%s) -> (%s)\n",url,jsonstr); + json = cJSON_Parse(jsonstr); + free(jsonstr); + } + return(json); +} + +////////////////////////////////////////////// +// start of dapp +////////////////////////////////////////////// + +uint64_t get_btcusd() +{ + cJSON *pjson,*bpi,*usd; char str[512]; uint64_t x,newprice,mult,btcusd = 0; + if ( (pjson= get_urljson((char *)"http://api.coindesk.com/v1/bpi/currentprice.json",(char *)"/tmp/oraclefeed.json")) != 0 ) + { + if ( (bpi= jobj(pjson,(char *)"bpi")) != 0 && (usd= jobj(bpi,(char *)"USD")) != 0 ) + { + btcusd = jdouble(usd,(char *)"rate_float") * SATOSHIDEN; + mult = 10000 + Net_change*10; + newprice = (btcusd * mult) / 10000; + x = ((uint64_t)time(NULL) << 32) | ((newprice / 10000) & 0xffffffff); + sprintf(str,"BTC/USD %.4f -> Betsize %.8f (^ / to change) && %.4f Net %.1f%% [+ - to change]\n",dstr(btcusd),dstr(Betsize),dstr(newprice),(double)100*(mult-10000)/10000); + mvaddstr(0, 0, str); + clrtoeol(); + doupdate(); + } + free_json(pjson); + } + return(x); +} + +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} + +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) +{ + static FILE *fp; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + rs->buffered[rs->num++] = c; + if ( 1 ) + { + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",(uint8_t)c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",(uint16_t)c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",(uint32_t)c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); + } else return(0); +} + +int32_t issue_bet(struct games_state *rs,int64_t x,int64_t betsize) +{ + char params[512],hexstr[64],*retstr; cJSON *retjson,*resobj; int32_t i,retval = -1; + memset(hexstr,0,sizeof(hexstr)); + for (i=0; i<8; i++) + { + sprintf(&hexstr[i<<1],"%02x",(uint8_t)(x & 0xff)); + x >>= 8; + } + sprintf(params,"[\"bet\",\"17\",\"[%.8f,%%22%s%%22]\"]",dstr(betsize),hexstr); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + //fprintf(stderr,"%s\n",jprint(resobj,0)); + } + free_json(retjson); + } + free(retstr); + } + return(retval); +} + +int prices(int argc, char **argv) +{ + struct games_state *rs = &globalR; + int32_t c,skipcount=0; uint32_t eventid = 0; + memset(rs,0,sizeof(*rs)); + rs->guiflag = 1; + rs->sleeptime = 1; // non-zero to allow refresh() + if ( argc >= 2 && strlen(argv[2]) == 64 ) + { +#ifdef _WIN32 +#ifdef _MSC_VER + rs->origseed = _strtoui64(argv[1], NULL, 10); +#else + rs->origseed = atol(argv[1]); // windows, but not MSVC +#endif // _MSC_VER +#else + rs->origseed = atol(argv[1]); // non-windows +#endif // _WIN32 + rs->seed = rs->origseed; + if ( argc >= 3 ) + { + strcpy(Gametxidstr,argv[2]); + fprintf(stderr,"setplayerdata %s\n",Gametxidstr); + if ( games_setplayerdata(rs,Gametxidstr) < 0 ) + { + fprintf(stderr,"invalid gametxid, or already started\n"); + return(-1); + } + } + } else rs->seed = 777; + gamesiterate(rs); + //gamesbailout(rs); + return 0; +} + +#endif + diff --git a/src/cc/games/prices.cpp b/src/cc/games/prices.cpp new file mode 100644 index 000000000..5c8437e5d --- /dev/null +++ b/src/cc/games/prices.cpp @@ -0,0 +1,281 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +std::string MYCCLIBNAME = (char *)"prices"; + +#define PRICES_BETPERIOD 3 +UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag); +extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; + +#define bstr(x) ((double)((uint32_t)x) / 10000.) + +struct prices_bar +{ + uint64_t open,high,low,close,sum; + int32_t num; +}; + +int32_t prices_barupdate(struct prices_bar *bar,uint64_t pricebits) +{ + uint32_t uprice,timestamp; + timestamp = (uint32_t)(pricebits >> 32); + uprice = (uint32_t)pricebits; + bar->sum += uprice, bar->num++; + if ( bar->open == 0 ) + bar->open = bar->high = bar->low = pricebits; + if ( uprice > (uint32_t)bar->high ) + bar->high = pricebits; + else if ( uprice < (uint32_t)bar->low ) + bar->low = pricebits; + bar->close = pricebits; + return(0); +} + +int64_t prices_bardist(struct prices_bar *bar,uint32_t aveprice,uint64_t pricebits) +{ + int64_t a,dist = 0; + if ( aveprice != 0 ) + { + a = (pricebits & 0xffffffff); + dist = (a - aveprice); + dist *= dist; + //fprintf(stderr,"dist.%lld (u %u - ave %u) %d\n",(long long)dist,uprice,aveprice,uprice-aveprice); + } + return(dist); +} + +void prices_bardisp(struct prices_bar *bar) +{ + if ( bar->num == 0 ) + fprintf(stderr,"BAR null\n"); + else fprintf(stderr,"BAR ave %.4f (O %.4f, H %.4f, L %.4f, C %.4f)\n",bstr(bar->sum/bar->num),bstr(bar->open),bstr(bar->high),bstr(bar->low),bstr(bar->close)); +} + +int64_t prices_blockinfo(int32_t height,char *acaddr) +{ + std::vector vopret; CBlockIndex *pindex; CBlock block; CTransaction tx,vintx; uint64_t pricebits; char destaddr[64]; uint32_t aveprice=0,timestamp,uprice; uint256 hashBlock; int64_t dist,mindist=(1LL<<60),prizefund = 0; int32_t mini=-1,i,n,vini,numvouts,iter; struct prices_bar refbar; + if ( (pindex= komodo_chainactive(height)) != 0 ) + { + if ( komodo_blockload(block,pindex) == 0 ) + { + n = block.vtx.size(); + vini = 0; + memset(&refbar,0,sizeof(refbar)); + for (iter=0; iter<2; iter++) + { + for (i=0; i= vintx.vout.size() || Getscriptaddress(destaddr,vintx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ) + continue; + else if ( (numvouts= tx.vout.size()) > 1 && tx.vout[numvouts-1].scriptPubKey[0] == 0x6a ) + { + GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); + if ( vopret.size() == 8 ) + { + E_UNMARSHAL(vopret,ss >> pricebits); + timestamp = (uint32_t)(pricebits >> 32); + uprice = (uint32_t)pricebits; + if ( iter == 0 ) + { + prizefund += tx.vout[0].nValue; + if ( strcmp(acaddr,destaddr) == 0 ) + { + //fprintf(stderr,"REF "); + prices_barupdate(&refbar,pricebits); + } + } + else if ( strcmp(acaddr,destaddr) != 0 ) + { + dist = prices_bardist(&refbar,aveprice,pricebits); + if ( dist < mindist ) + { + mindist = dist; + mini = i; + } + fprintf(stderr,"mini.%d i.%d %.8f t%u %.4f v.%d %s lag.%d i.%d dist.%lld\n",mini,i,(double)tx.vout[0].nValue/COIN,timestamp,(double)uprice/10000,numvouts,destaddr,(int32_t)(pindex->nTime-timestamp),iter,(long long)dist); + } + } else return(-3); + } + } + if ( iter == 0 ) + { + prices_bardisp(&refbar); + if ( refbar.num != 0 ) + aveprice = (uint32_t)refbar.sum / refbar.num; + } + } + return(prizefund); + } else return(-2); + } else return(-1); +} + +UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); char acaddr[64]; CPubKey acpk,mypk,gamespk; int64_t prizefund = 0; int32_t height,nextheight = komodo_nextheight(); + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error"," no -ac_pubkey for price reference")); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); + Getscriptaddress(acaddr,CScript() << ParseHex(HexStr(acpk)) << OP_CHECKSIG); + if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + { + height = juint(jitem(params,0),0); + result.push_back(Pair("height",(int64_t)height)); + if ( (prizefund= prices_blockinfo(height,acaddr)) < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("errorcode",prizefund)); + result.push_back(Pair("error","blockinfo error")); + } + else + { + // display bets + if ( height <= nextheight-PRICES_BETPERIOD ) + { + // settle bets by first nonzero reference bar + } + result.push_back(Pair("prizefund",ValueFromAmount(prizefund))); + result.push_back(Pair("result","success")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse")); + } + return(result); +} + +UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; uint64_t price; CPubKey gamespk,mypk,acpk; + if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error"," no -ac_pubkey for price reference")); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + acpk = buf2pk(ASSETCHAINS_OVERRIDE_PUBKEY33); + if ( params != 0 && cJSON_GetArraySize(params) == 2 ) + { + amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; + if ( cclib_parsehash((uint8_t *)&price,jitem(params,1),8) < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parsehash")); + return(result); + } + if ( mypk == acpk ) + { + amount = 0; // i am the reference price feed + //fprintf(stderr,"i am the reference\n"); + } + //fprintf(stderr,"amount %llu price %llx\n",(long long)amount,(long long)price); + if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,CScript() << OP_RETURN << price); + return(games_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse")); + } + return(result); +} + +void prices_update(uint32_t timestamp,uint32_t uprice,int32_t ismine) +{ + //fprintf(stderr,"%s t%u %.4f %16llx\n",ismine!=0?"mine":"ext ",timestamp,(double)uprice/10000,(long long)((uint64_t)timestamp<<32) | uprice); +} + +// game specific code for daemon +void games_packitemstr(char *packitemstr,struct games_packitem *item) +{ + strcpy(packitemstr,""); +} + +int64_t games_cashout(struct games_player *P) +{ + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; + cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; + return(cashout); +} + +void pricesplayerjson(UniValue &obj,struct games_player *P) +{ + obj.push_back(Pair("packsize",(int64_t)P->packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); + obj.push_back(Pair("level",(int64_t)P->level)); + obj.push_back(Pair("experience",(int64_t)P->experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); +} + +int32_t disp_gamesplayer(char *str,struct games_player *P) +{ + str[0] = 0; + //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + // return(-1); + sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); + return(0); +} + +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) +{ + uint256 gametxid; int32_t i,len; char str[67]; int64_t price; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) + { + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + for (i=0; i> 32),(uint32_t)(price & 0xffffffff),pk == pubkey2pk(Mypubkey())); + //fprintf(stderr,"%llu -> t%u %.4f ",(long long)price,(uint32_t)(price >> 32),(double)(price & 0xffffffff)/10000); + //fprintf(stderr," got payload, from %s %s/e%d\n",pubkey33_str(str,(uint8_t *)&pk),gametxid.GetHex().c_str(),eventid); + return(0); + } else return(-1); +} + +bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + return(true); +} + diff --git a/src/cc/games/prices.h b/src/cc/games/prices.h new file mode 100644 index 000000000..ae1c46821 --- /dev/null +++ b/src/cc/games/prices.h @@ -0,0 +1,208 @@ + +#ifndef H_PRICES_H +#define H_PRICES_H + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + +/* + Convert a tetromino type to its corresponding cell. + */ +#define TYPE_TO_CELL(x) ((x)+1) + +/* + Strings for how you would print a tetris board. + */ +#define TC_EMPTY_STR " " +#define TC_BLOCK_STR "\u2588" + +/* + Questions about a tetris cell. + */ +#define TC_IS_EMPTY(x) ((x) == TC_EMPTY) +#define TC_IS_FILLED(x) (!TC_IS_EMPTY(x)) + +/* + How many cells in a tetromino? + */ +#define TETRIS 4 +/* + How many tetrominos? + */ +#define NUM_TETROMINOS 7 +/* + How many orientations of a tetromino? + */ +#define NUM_ORIENTATIONS 4 + +/* + Level constants. + */ +#define MAX_LEVEL 19 +#define LINES_PER_LEVEL 10 + +/* + A "cell" is a 1x1 block within a tetris board. + */ +typedef enum { + TC_EMPTY, TC_CELLI, TC_CELLJ, TC_CELLL, TC_CELLO, TC_CELLS, TC_CELLT, TC_CELLZ +} tetris_cell; + +/* + A "type" is a type/shape of a tetromino. Not including orientation. + */ +typedef enum { + TET_I, TET_J, TET_L, TET_O, TET_S, TET_T, TET_Z +} tetris_type; + +/* + A row,column pair. Negative numbers allowed, because we need them for + offsets. + */ +typedef struct { + int row; + int col; +} tetris_location; + +/* + A "block" is a struct that contains information about a tetromino. + Specifically, what type it is, what orientation it has, and where it is. + */ +typedef struct { + int typ; + int ori; + tetris_location loc; +} tetris_block; + +/* + All possible moves to give as input to the game. + */ +typedef enum { + TM_LEFT, TM_RIGHT, TM_CLOCK, TM_COUNTER, TM_DROP, TM_HOLD, TM_NONE +} tetris_move; + +/* + A game object! + */ +typedef struct { + /* + Game board stuff: + */ + int rows; + int cols; + /* + Scoring information: + */ + int points; + int level; + /* + Falling block is the one currently going down. Next block is the one that + will be falling after this one. Stored is the block that you can swap out. + */ + tetris_block falling; + tetris_block next; + tetris_block stored; + /* + Number of game ticks until the block will move down. + */ + int ticks_till_gravity; + /* + Number of lines until you advance to the next level. + */ + int lines_remaining; + char board[]; +} tetris_game; + +/* + This array stores all necessary information about the cells that are filled by + each tetromino. The first index is the type of the tetromino (i.e. shape, + e.g. I, J, Z, etc.). The next index is the orientation (0-3). The final + array contains 4 tetris_location objects, each mapping to an offset from a + point on the upper left that is the tetromino "origin". + */ +extern const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS]; + +/* + This array tells you how many ticks per gravity by level. Decreases as level + increases, to add difficulty. + */ +extern const int GRAVITY_LEVEL[MAX_LEVEL+1]; + +// Data structure manipulation. +void tg_init(tetris_game *obj, int rows, int cols); +tetris_game *tg_create(struct games_state *rs,int rows, int cols); +void tg_destroy(tetris_game *obj); +void tg_delete(tetris_game *obj); +tetris_game *tg_load(FILE *f); +void tg_save(tetris_game *obj, FILE *f); + +// Public methods not related to memory: +char tg_get(tetris_game *obj, int row, int col); +bool tg_check(tetris_game *obj, int row, int col); +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move); +void tg_print(tetris_game *obj, FILE *f); + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ +#define GAMENAME "prices" // name of executable +#define GAMEMAIN prices // main program of game +#define GAMEPLAYERJSON pricesplayerjson // displays game specific json +#define GAMEDATA pricesdata // extracts data from game specific variables into games_state +#define CHAINNAME "PRICES" // -ac_name= +typedef uint64_t gamesevent; // can be 8, 16, 32, or 64 bits + +#define MAXPACK 23 +struct games_packitem +{ + int32_t type,launch,count,which,hplus,dplus,arm,flags,group; + char damage[8],hurldmg[8]; +}; + +struct games_player +{ + int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; + struct games_packitem gamespack[MAXPACK]; +}; + +struct games_state +{ + uint64_t seed,origseed; + char *keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct games_player P; + gamesevent buffered[5000],*keystrokes; + uint8_t playerdata[8192]; +}; +extern struct games_state globalR; +void *gamesiterate(struct games_state *rs); +int32_t flushkeystrokes(struct games_state *rs,int32_t waitflag); + +void games_packitemstr(char *packitemstr,struct games_packitem *item); +uint64_t _games_rngnext(uint64_t initseed); +int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis); +gamesevent games_revendian(gamesevent revx); +int32_t disp_gamesplayer(char *str,struct games_player *P); + +#endif + diff --git a/src/cc/games/tetris.c b/src/cc/games/tetris.c new file mode 100644 index 000000000..8e7642837 --- /dev/null +++ b/src/cc/games/tetris.c @@ -0,0 +1,904 @@ + +#include "tetris.h" + +/* + In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. + + also, the standalone game needs to support argv of seed gametxid, along with replay args + */ + +int random_tetromino(struct games_state *rs) +{ + rs->seed = _games_rngnext(rs->seed); + return(rs->seed % NUM_TETROMINOS); +} + +int32_t tetrisdata(struct games_player *P,void *ptr) +{ + tetris_game *tg = (tetris_game *)ptr; + P->gold = tg->points; + P->dungeonlevel = tg->level; + //fprintf(stderr,"score.%d level.%d\n",tg->points,tg->level); + return(0); +} + +/***************************************************************************/ +/** https://github.com/brenns10/tetris + @file main.c + @author Stephen Brennan + @date Created Wednesday, 10 June 2015 + @brief Main program for tetris. + @copyright Copyright (c) 2015, Stephen Brennan. Released under the Revised + BSD License. See LICENSE.txt for details. + *******************************************************************************/ + + +#include // for FILE +#include // for bool +#include +#include +#include +#include +#include +#include + +#ifdef BUILD_GAMESCC +#include "../rogue/cursesd.h" +#else +#include +#endif + + +#define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) +#define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) + +/******************************************************************************* + Array Definitions + *******************************************************************************/ + +const tetris_location TETROMINOS[NUM_TETROMINOS][NUM_ORIENTATIONS][TETRIS] = +{ + // I + {{{1, 0}, {1, 1}, {1, 2}, {1, 3}}, + {{0, 2}, {1, 2}, {2, 2}, {3, 2}}, + {{3, 0}, {3, 1}, {3, 2}, {3, 3}}, + {{0, 1}, {1, 1}, {2, 1}, {3, 1}}}, + // J + {{{0, 0}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 2}}, + {{0, 1}, {1, 1}, {2, 0}, {2, 1}}}, + // L + {{{0, 2}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {2, 1}, {2, 2}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 0}}, + {{0, 0}, {0, 1}, {1, 1}, {2, 1}}}, + // O + {{{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}, + {{0, 1}, {0, 2}, {1, 1}, {1, 2}}}, + // S + {{{0, 1}, {0, 2}, {1, 0}, {1, 1}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 2}}, + {{1, 1}, {1, 2}, {2, 0}, {2, 1}}, + {{0, 0}, {1, 0}, {1, 1}, {2, 1}}}, + // T + {{{0, 1}, {1, 0}, {1, 1}, {1, 2}}, + {{0, 1}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {1, 2}, {2, 1}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 1}}}, + // Z + {{{0, 0}, {0, 1}, {1, 1}, {1, 2}}, + {{0, 2}, {1, 1}, {1, 2}, {2, 1}}, + {{1, 0}, {1, 1}, {2, 1}, {2, 2}}, + {{0, 1}, {1, 0}, {1, 1}, {2, 0}}}, +}; + +const int GRAVITY_LEVEL[MAX_LEVEL+1] = { + // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 50, 48, 46, 44, 42, 40, 38, 36, 34, 32, + //10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 30, 28, 26, 24, 22, 20, 16, 12, 8, 4 +}; + +/******************************************************************************* + Helper Functions for Blocks + *******************************************************************************/ + +void sleep_milli(int milliseconds) +{ + struct timespec ts; + ts.tv_sec = 0; + ts.tv_nsec = milliseconds * 1000 * 1000; + nanosleep(&ts, NULL); +} + +/* + Return the block at the given row and column. + */ +char tg_get(tetris_game *obj, int row, int column) +{ + return obj->board[obj->cols * row + column]; +} + +/* + Set the block at the given row and column. + */ +static void tg_set(tetris_game *obj, int row, int column, char value) +{ + obj->board[obj->cols * row + column] = value; +} + +/* + Check whether a row and column are in bounds. + */ +bool tg_check(tetris_game *obj, int row, int col) +{ + return 0 <= row && row < obj->rows && 0 <= col && col < obj->cols; +} + +/* + Place a block onto the board. + */ +static void tg_put(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, + TYPE_TO_CELL(block.typ)); + } +} + +/* + Clear a block out of the board. + */ +static void tg_remove(tetris_game *obj, tetris_block block) +{ + int i; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + tg_set(obj, block.loc.row + cell.row, block.loc.col + cell.col, TC_EMPTY); + } +} + +/* + Check if a block can be placed on the board. + */ +static bool tg_fits(tetris_game *obj, tetris_block block) +{ + int i, r, c; + for (i = 0; i < TETRIS; i++) { + tetris_location cell = TETROMINOS[block.typ][block.ori][i]; + r = block.loc.row + cell.row; + c = block.loc.col + cell.col; + if (!tg_check(obj, r, c) || TC_IS_FILLED(tg_get(obj, r, c))) { + return false; + } + } + return true; +} + +/* + Create a new falling block and populate the next falling block with a random + one. + */ +static void tg_new_falling(struct games_state *rs,tetris_game *obj) +{ + // Put in a new falling tetromino. + obj->falling = obj->next; + obj->next.typ = random_tetromino(rs); + obj->next.ori = 0; + obj->next.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; +} + +/******************************************************************************* + Game Turn Helpers + *******************************************************************************/ + +/* + Tick gravity, and move the block down if gravity should act. + */ +static void tg_do_gravity_tick(struct games_state *rs,tetris_game *obj) +{ + obj->ticks_till_gravity--; + if (obj->ticks_till_gravity <= 0) { + tg_remove(obj, obj->falling); + obj->falling.loc.row++; + if (tg_fits(obj, obj->falling)) { + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + } else { + obj->falling.loc.row--; + tg_put(obj, obj->falling); + + tg_new_falling(rs,obj); + } + tg_put(obj, obj->falling); + } +} + +/* + Move the falling tetris block left (-1) or right (+1). + */ +static void tg_move(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + obj->falling.loc.col += direction; + if (!tg_fits(obj, obj->falling)) { + obj->falling.loc.col -= direction; + } + tg_put(obj, obj->falling); +} + +/* + Send the falling tetris block to the bottom. + */ +static void tg_down(struct games_state *rs,tetris_game *obj) +{ + tg_remove(obj, obj->falling); + while (tg_fits(obj, obj->falling)) { + obj->falling.loc.row++; + } + obj->falling.loc.row--; + tg_put(obj, obj->falling); + tg_new_falling(rs,obj); +} + +/* + Rotate the falling block in either direction (+/-1). + */ +static void tg_rotate(tetris_game *obj, int direction) +{ + tg_remove(obj, obj->falling); + + while (true) { + obj->falling.ori = (obj->falling.ori + direction) % NUM_ORIENTATIONS; + + // If the new orientation fits, we're done. + if (tg_fits(obj, obj->falling)) + break; + + // Otherwise, try moving left to make it fit. + obj->falling.loc.col--; + if (tg_fits(obj, obj->falling)) + break; + + // Finally, try moving right to make it fit. + obj->falling.loc.col += 2; + if (tg_fits(obj, obj->falling)) + break; + + // Put it back in its original location and try the next orientation. + obj->falling.loc.col--; + // Worst case, we come back to the original orientation and it fits, so this + // loop will terminate. + } + + tg_put(obj, obj->falling); +} + +/* + Swap the falling block with the block in the hold buffer. + */ +static void tg_hold(struct games_state *rs,tetris_game *obj) +{ + tg_remove(obj, obj->falling); + if (obj->stored.typ == -1) { + obj->stored = obj->falling; + tg_new_falling(rs,obj); + } else { + int typ = obj->falling.typ, ori = obj->falling.ori; + obj->falling.typ = obj->stored.typ; + obj->falling.ori = obj->stored.ori; + obj->stored.typ = typ; + obj->stored.ori = ori; + while (!tg_fits(obj, obj->falling)) { + obj->falling.loc.row--; + if (tg_fits(obj, obj->falling)) { + break; + } + obj->falling.loc.col--; + if (tg_fits(obj, obj->falling)) { + break; + } + obj->falling.loc.col += 2; + } + } + tg_put(obj, obj->falling); +} + +/* + Perform the action specified by the move. + */ +static void tg_handle_move(struct games_state *rs,tetris_game *obj, tetris_move move) +{ + switch (move) { + case TM_LEFT: + //fprintf(stderr,"LEFT "); + tg_move(obj, -1); + break; + case TM_RIGHT: + //fprintf(stderr,"RIGHT "); + tg_move(obj, 1); + break; + case TM_DROP: + tg_down(rs,obj); + break; + case TM_CLOCK: + tg_rotate(obj, 1); + break; + case TM_COUNTER: + tg_rotate(obj, -1); + break; + case TM_HOLD: + tg_hold(rs,obj); + break; + default: + // pass + break; + } +} + +/* + Return true if line i is full. + */ +static bool tg_line_full(tetris_game *obj, int i) +{ + int j; + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) + return false; + } + return true; +} + +/* + Shift every row above r down one. + */ +static void tg_shift_lines(tetris_game *obj, int r) +{ + int i, j; + for (i = r-1; i >= 0; i--) { + for (j = 0; j < obj->cols; j++) { + tg_set(obj, i+1, j, tg_get(obj, i, j)); + tg_set(obj, i, j, TC_EMPTY); + } + } +} + +/* + Find rows that are filled, remove them, shift, and return the number of + cleared rows. + */ +static int tg_check_lines(tetris_game *obj) +{ + int i, nlines = 0; + tg_remove(obj, obj->falling); // don't want to mess up falling block + + for (i = obj->rows-1; i >= 0; i--) { + if (tg_line_full(obj, i)) { + tg_shift_lines(obj, i); + i++; // do this line over again since they're shifted + nlines++; + } + } + + tg_put(obj, obj->falling); // replace + return nlines; +} + +/* + Adjust the score for the game, given how many lines were just cleared. + */ +static void tg_adjust_score(tetris_game *obj, int lines_cleared) +{ + static int line_multiplier[] = {0, 40, 100, 300, 1200}; + obj->points += line_multiplier[lines_cleared] * (obj->level + 1); + if (lines_cleared >= obj->lines_remaining) { + obj->level = MIN(MAX_LEVEL, obj->level + 1); + lines_cleared -= obj->lines_remaining; + obj->lines_remaining = LINES_PER_LEVEL - lines_cleared; + } else { + obj->lines_remaining -= lines_cleared; + } +} + +/* + Return true if the game is over. + */ +static bool tg_game_over(tetris_game *obj) +{ + int i, j; + bool over = false; + tg_remove(obj, obj->falling); + for (i = 0; i < 2; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + over = true; + } + } + } + tg_put(obj, obj->falling); + return over; +} + +/******************************************************************************* + Main Public Functions + *******************************************************************************/ + +/* + Do a single game tick: process gravity, user input, and score. Return true if + the game is still running, false if it is over. + */ +bool tg_tick(struct games_state *rs,tetris_game *obj, tetris_move move) +{ + int lines_cleared; + // Handle gravity. + tg_do_gravity_tick(rs,obj); + + // Handle input. + tg_handle_move(rs,obj, move); + + // Check for cleared lines + lines_cleared = tg_check_lines(obj); + + tg_adjust_score(obj, lines_cleared); + + // Return whether the game will continue (NOT whether it's over) + return !tg_game_over(obj); +} + +void tg_init(struct games_state *rs,tetris_game *obj, int rows, int cols) +{ + // Initialization logic + obj->rows = rows; + obj->cols = cols; + //obj->board = (char *)malloc(rows * cols); + memset(obj->board, TC_EMPTY, rows * cols); + obj->points = 0; + obj->level = 0; + obj->ticks_till_gravity = GRAVITY_LEVEL[obj->level]; + obj->lines_remaining = LINES_PER_LEVEL; + //srand(time(NULL)); + tg_new_falling(rs,obj); + tg_new_falling(rs,obj); + obj->stored.typ = -1; + obj->stored.ori = 0; + obj->stored.loc.row = 0; + obj->next.loc.col = obj->cols/2 - 2; + //printf("%d", obj->falling.loc.col); +} + +tetris_game *tg_create(struct games_state *rs,int rows, int cols) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game) + rows*cols); + tg_init(rs,obj, rows, cols); + return obj; +} + +/*void tg_destroy(tetris_game *obj) +{ + // Cleanup logic + free(obj->board); +}*/ + +void tg_delete(tetris_game *obj) { + //tg_destroy(obj); + free(obj); +} + +/* + Load a game from a file. + +tetris_game *tg_load(FILE *f) +{ + tetris_game *obj = (tetris_game *)malloc(sizeof(tetris_game)); + if (fread(obj, sizeof(tetris_game), 1, f) != 1 ) + { + fprintf(stderr,"read game error\n"); + free(obj); + obj = 0; + } + else + { + obj->board = (char *)malloc(obj->rows * obj->cols); + if (fread(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + { + fprintf(stderr,"fread error\n"); + free(obj->board); + free(obj); + obj = 0; + } + } + return obj; +}*/ + +/* + Save a game to a file. + +void tg_save(tetris_game *obj, FILE *f) +{ + if (fwrite(obj, sizeof(tetris_game), 1, f) != 1 ) + fprintf(stderr,"error writing tetrisgame\n"); + else if (fwrite(obj->board, sizeof(char), obj->rows * obj->cols, f) != obj->rows * obj->cols ) + fprintf(stderr,"error writing board\n"); +}*/ + +/* + Print a game board to a file. Really just for early debugging. + */ +void tg_print(tetris_game *obj, FILE *f) { + int i, j; + for (i = 0; i < obj->rows; i++) { + for (j = 0; j < obj->cols; j++) { + if (TC_IS_EMPTY(tg_get(obj, i, j))) { + fputs(TC_EMPTY_STR, f); + } else { + fputs(TC_BLOCK_STR, f); + } + } + fputc('\n', f); + } +} + +/* + 2 columns per cell makes the game much nicer. + */ +#define COLS_PER_CELL 2 +/* + Macro to print a cell of a specific type to a window. + */ +#define ADD_BLOCK(w,x) waddch((w),' '|A_REVERSE|COLOR_PAIR(x)); \ +waddch((w),' '|A_REVERSE|COLOR_PAIR(x)) +#define ADD_EMPTY(w) waddch((w), ' '); waddch((w), ' ') + +/* + Print the tetris board onto the ncurses window. + */ +void display_board(WINDOW *w, tetris_game *obj) +{ + int i, j; + box(w, 0, 0); + for (i = 0; i < obj->rows; i++) { + wmove(w, 1 + i, 1); + for (j = 0; j < obj->cols; j++) { + if (TC_IS_FILLED(tg_get(obj, i, j))) { + ADD_BLOCK(w,tg_get(obj, i, j)); + } else { + ADD_EMPTY(w); + } + } + } + wnoutrefresh(w); +} + +/* + Display a tetris piece in a dedicated window. + */ +void display_piece(WINDOW *w, tetris_block block) +{ + int b; + tetris_location c; + wclear(w); + box(w, 0, 0); + if (block.typ == -1) { + wnoutrefresh(w); + return; + } + for (b = 0; b < TETRIS; b++) { + c = TETROMINOS[block.typ][block.ori][b]; + wmove(w, c.row + 1, c.col * COLS_PER_CELL + 1); + ADD_BLOCK(w, TYPE_TO_CELL(block.typ)); + } + wnoutrefresh(w); +} + +/* + Display score information in a dedicated window. + */ +void display_score(WINDOW *w, tetris_game *tg) +{ + wclear(w); + box(w, 0, 0); + wprintw(w, (char *)"Score\n%d\n", tg->points); + wprintw(w, (char *)"Level\n%d\n", tg->level); + wprintw(w, (char *)"Lines\n%d\n", tg->lines_remaining); + wnoutrefresh(w); +} + +/* + Save and exit the game. + +void save(tetris_game *game, WINDOW *w) +{ + FILE *f; + + wclear(w); + box(w, 0, 0); // return the border + wmove(w, 1, 1); + wprintw(w, (char *)"Save and exit? [Y/n] "); + wrefresh(w); + timeout(-1); + if (getch() == 'n') { + timeout(0); + return; + } + f = fopen("tetris.save", "w"); + tg_save(game, f); + fclose(f); + tg_delete(game); + endwin(); + fprintf(stderr,"Game saved to \"tetris.save\".\n"); + fprintf(stderr,"Resume by passing the filename as an argument to this program.\n"); + exit(EXIT_SUCCESS); +}*/ + +/* + Do the NCURSES initialization steps for color blocks. + */ +void init_colors(void) +{ + start_color(); + //init_color(COLOR_ORANGE, 1000, 647, 0); + init_pair(TC_CELLI, COLOR_CYAN, COLOR_BLACK); + init_pair(TC_CELLJ, COLOR_BLUE, COLOR_BLACK); + init_pair(TC_CELLL, COLOR_WHITE, COLOR_BLACK); + init_pair(TC_CELLO, COLOR_YELLOW, COLOR_BLACK); + init_pair(TC_CELLS, COLOR_GREEN, COLOR_BLACK); + init_pair(TC_CELLT, COLOR_MAGENTA, COLOR_BLACK); + init_pair(TC_CELLZ, COLOR_RED, COLOR_BLACK); +} + +struct games_state globalR; +extern char Gametxidstr[]; +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c); +gamesevent games_readevent(struct games_state *rs); + +void *gamesiterate(struct games_state *rs) +{ + uint32_t counter = 0; bool running = true; tetris_move move = TM_NONE; + gamesevent c; uint16_t skipcount=0; int32_t prevlevel; uint32_t eventid = 0; tetris_game *tg; + WINDOW *board, *next, *hold, *score; + if ( rs->guiflag != 0 || rs->sleeptime != 0 ) + { + // NCURSES initialization: + initscr(); // initialize curses + cbreak(); // pass key presses to program, but not signals + noecho(); // don't echo key presses to screen + keypad(stdscr, TRUE); // allow arrow keys + timeout(0); // no blocking on getch() + curs_set(0); // set the cursor to invisible + init_colors(); // setup tetris colors + } + tg = tg_create(rs,22, 10); + prevlevel = tg->level; + // Create windows for each section of the interface. + board = newwin(tg->rows + 2, 2 * tg->cols + 2, 0, 0); + next = newwin(6, 10, 0, 2 * (tg->cols + 1) + 1); + hold = newwin(6, 10, 7, 2 * (tg->cols + 1) + 1); + score = newwin(6, 10, 14, 2 * (tg->cols + 1 ) + 1); + while ( running != 0 ) + { + running = tg_tick(rs,tg,move); + if ( 1 && (rs->guiflag != 0 || rs->sleeptime != 0) ) + { + display_board(board,tg); + display_piece(next,tg->next); + display_piece(hold,tg->stored); + display_score(score,tg); + } + if ( rs->guiflag != 0 ) + { +#ifdef STANDALONE + sleep_milli(15); + if ( (counter++ % 10) == 0 ) + doupdate(); + c = games_readevent(rs); + if ( c <= 0x7f || skipcount == 0x3fff ) + { + if ( skipcount > 0 ) + issue_games_events(rs,Gametxidstr,eventid-skipcount,skipcount | 0x4000); + if ( c <= 0x7f ) + issue_games_events(rs,Gametxidstr,eventid,c); + if ( tg->level != prevlevel ) + { + flushkeystrokes(rs,0); + prevlevel = tg->level; + } + skipcount = 0; + } else skipcount++; +#endif + } + else + { + if ( rs->replaydone != 0 ) + break; + if ( rs->sleeptime != 0 ) + { + sleep_milli(1); + if ( (counter++ % 20) == 0 ) + doupdate(); + } + if ( skipcount == 0 ) + { + c = games_readevent(rs); + //fprintf(stderr,"%04x score.%d level.%d\n",c,tg->points,tg->level); + if ( (c & 0x4000) == 0x4000 ) + { + skipcount = (c & 0x3fff); + c = 'S'; + } + } + if ( skipcount > 0 ) + skipcount--; + } + eventid++; + switch ( c ) + { + case 'h': + move = TM_LEFT; + break; + case 'l': + move = TM_RIGHT; + break; + case 'k': + move = TM_CLOCK; + break; + case 'j': + move = TM_DROP; + break; + case 'q': + running = false; + move = TM_NONE; + break; + /*case 'p': + wclear(board); + box(board, 0, 0); + wmove(board, tg->rows/2, (tg->cols*COLS_PER_CELL-6)/2); + wprintw(board, "PAUSED"); + wrefresh(board); + timeout(-1); + getch(); + timeout(0); + move = TM_NONE; + break; + case 's': + save(tg, board); + move = TM_NONE; + break;*/ + case ' ': + move = TM_HOLD; + break; + default: + move = TM_NONE; + } + } + return(tg); +} + +#ifdef STANDALONE +/* + Main tetris game! + */ +#include "dapps/dappstd.c" + + +char *clonestr(char *str) +{ + char *clone; int32_t len; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + len = strlen(str); + clone = (char *)calloc(1,len+16); + strcpy(clone,str); + return(clone); +} + +int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eventid,gamesevent c) +{ + static FILE *fp; + char params[512],*retstr; cJSON *retjson,*resobj; int32_t retval = -1; + if ( fp == 0 ) + fp = fopen("events.log","wb"); + rs->buffered[rs->num++] = c; + if ( 0 ) + { + if ( sizeof(c) == 1 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%02x%%22,%%22%s%%22,%u]\"]",(uint8_t)c&0xff,gametxidstr,eventid); + else if ( sizeof(c) == 2 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%04x%%22,%%22%s%%22,%u]\"]",(uint16_t)c&0xffff,gametxidstr,eventid); + else if ( sizeof(c) == 4 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%08x%%22,%%22%s%%22,%u]\"]",(uint32_t)c&0xffffffff,gametxidstr,eventid); + else if ( sizeof(c) == 8 ) + sprintf(params,"[\"events\",\"17\",\"[%%22%016llx%%22,%%22%s%%22,%u]\"]",(long long)c,gametxidstr,eventid); + if ( (retstr= komodo_issuemethod(USERPASS,(char *)"cclib",params,GAMES_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,(char *)"result")) != 0 ) + { + retval = 0; + if ( fp != 0 ) + { + fprintf(fp,"%s\n",jprint(resobj,0)); + fflush(fp); + } + } + free_json(retjson); + } else fprintf(fp,"error parsing %s\n",retstr); + free(retstr); + } else fprintf(fp,"error issuing method %s\n",params); + return(retval); + } else return(0); +} + +int tetris(int argc, char **argv) +{ + struct games_state *rs = &globalR; + int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0; + memset(rs,0,sizeof(*rs)); + rs->guiflag = 1; + rs->sleeptime = 1; // non-zero to allow refresh() + if ( argc >= 2 && strlen(argv[2]) == 64 ) + { +#ifdef _WIN32 +#ifdef _MSC_VER + rs->origseed = _strtoui64(argv[1], NULL, 10); +#else + rs->origseed = atol(argv[1]); // windows, but not MSVC +#endif // _MSC_VER +#else + rs->origseed = atol(argv[1]); // non-windows +#endif // _WIN32 + rs->seed = rs->origseed; + if ( argc >= 3 ) + { + strcpy(Gametxidstr,argv[2]); + fprintf(stderr,"setplayerdata %s\n",Gametxidstr); + if ( games_setplayerdata(rs,Gametxidstr) < 0 ) + { + fprintf(stderr,"invalid gametxid, or already started\n"); + return(-1); + } + } + } else rs->seed = 777; + + /* Load file if given a filename. + if (argc >= 2) { + FILE *f = fopen(argv[1], "r"); + if (f == NULL) { + perror("tetris"); + exit(EXIT_FAILURE); + } + tg = tg_load(f); + fclose(f); + } else { + // Otherwise create new game. + tg = tg_create(rs,22, 10); + }*/ + + // Game loop + tg = (tetris_game *)gamesiterate(rs); + gamesbailout(rs); + // Deinitialize NCurses + wclear(stdscr); + endwin(); + // Output ending message. + printf("Game over!\n"); + printf("You finished with %d points on level %d.\n", tg->points, tg->level); + + // Deinitialize Tetris + tg_delete(tg); + return 0; +} + +#endif + diff --git a/src/cc/games/tetris.cpp b/src/cc/games/tetris.cpp new file mode 100644 index 000000000..cd609154d --- /dev/null +++ b/src/cc/games/tetris.cpp @@ -0,0 +1,87 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +std::string MYCCLIBNAME = (char *)"gamescc"; + +// game specific code for daemon +void games_packitemstr(char *packitemstr,struct games_packitem *item) +{ + strcpy(packitemstr,""); +} + +int64_t games_cashout(struct games_player *P) +{ + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=10000,cashout = 0; + cashout = (uint64_t)P->gold * mult; + return(cashout); +} + +void tetrisplayerjson(UniValue &obj,struct games_player *P) +{ + obj.push_back(Pair("packsize",(int64_t)P->packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); + obj.push_back(Pair("level",(int64_t)P->level)); + obj.push_back(Pair("experience",(int64_t)P->experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); +} + +int32_t disp_gamesplayer(char *str,struct games_player *P) +{ + str[0] = 0; + //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + // return(-1); + sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); + return(0); +} + +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) +{ + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) + { + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + //for (i=0; i> 16); + seeds[2] = (initseed >> 32); + seeds[3] = (initseed >> 48); + seeds[0] = (seeds[0]*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[1] = ((seeds[0] ^ seeds[1])*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[2] = ((seeds[0] ^ seeds[1] ^ seeds[2])*GAMES_RNGMULT + GAMES_RNGOFFSET); + seeds[3] = ((seeds[0] ^ seeds[1] ^ seeds[2] ^ seeds[3])*GAMES_RNGMULT + GAMES_RNGOFFSET); + return(((uint64_t)seeds[3] << 48) | ((uint64_t)seeds[2] << 24) | ((uint64_t)seeds[1] << 16) | seeds[0]); +} + +gamesevent games_revendian(gamesevent revx) +{ + int32_t i; gamesevent x = 0; + //fprintf(stderr,"%04x -> ",revx); + for (i=0; iguiflag == 0 ) + { + static uint32_t counter; + if ( rs->ind < rs->numkeys ) + { + ch = rs->keystrokes[rs->ind++]; + if ( 0 ) + { + static FILE *fp; static int32_t counter; + if ( fp == 0 ) + fp = fopen("log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%d: (%c) seed.%llu\n",counter,c,(long long)rs->origseed); + fflush(fp); + counter++; + } + } + return(ch); + } + if ( rs->replaydone != 0 && counter++ < 3 ) + fprintf(stderr,"replay finished but readchar called\n"); + rs->replaydone = (uint32_t)time(NULL); + return(0); + } + if ( rs == 0 || rs->guiflag != 0 ) + { + c = getch(); + switch ( c ) + { + case KEY_LEFT: + c = 'h'; + break; + case KEY_RIGHT: + c = 'l'; + break; + case KEY_UP: + c = 'k'; + break; + case KEY_DOWN: + c = 'j'; + break; + } + ch = c; + if (ch == 3) + { + //_quit(); + return(27); + } + } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); + return(ch); +} + +int32_t games_replay2(uint8_t *newdata,uint64_t seed,gamesevent *keystrokes,int32_t num,struct games_player *player,int32_t sleepmillis) +{ + struct games_state *rs; FILE *fp; int32_t i,n; void *ptr; + rs = (struct games_state *)calloc(1,sizeof(*rs)); + rs->seed = rs->origseed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = sleepmillis * 1000; + if ( player != 0 ) + { + rs->P = *player; + rs->restoring = 1; + if ( rs->P.packsize > MAXPACK ) + rs->P.packsize = MAXPACK; + } + globalR = *rs; + uint32_t starttime = (uint32_t)time(NULL); + ptr = gamesiterate(rs); + if ( 0 ) + { + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime); + sleep(2); + starttime = (uint32_t)time(NULL); + for (i=0; i<10000; i++) + { + memset(rs,0,sizeof(*rs)); + rs->seed = rs->origseed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = 0; + gamesiterate(rs); + } + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL)-starttime); + sleep(3); + } + // extract playerdata + + /*if ( (fp= fopen("checkfile","wb")) != 0 ) + { + //save_file(rs,fp,0); + if ( newdata != 0 && rs->playersize > 0 ) + memcpy(newdata,rs->playerdata,rs->playersize); + }*/ + if ( ptr != 0 ) + { + // extract data from ptr + if ( GAMEDATA(&rs->P,ptr) < 0 ) + memset(&rs->P,0,sizeof(rs->P)); + else + { + rs->playersize = sizeof(rs->P); + if ( newdata != 0 ) + memcpy(newdata,&rs->P,rs->playersize); + } + free(ptr); + } + n = rs->playersize; + //fprintf(stderr,"gold.%d\n",rs->P.gold); sleep(3); + free(rs); + return(n); +} + +#ifndef STANDALONE +#ifdef BUILD_PRICES +#include "games/prices.cpp" +#else +#include "games/tetris.cpp" +#endif + +void GAMEJSON(UniValue &obj,struct games_player *P); + +/* +./c cclib rng 17 \"[%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,250]\" +{ + "playerid": 250, + "seed": 1398876319979341887, + "rng": 14565767519458298868, + "lastrng": 15075236803740723044, + "maxrngs": 10000, + "result": "success" +} + + ./c cclib rngnext 17 \"[14565767519458298868]\" + { + "seed": 14565767519458297856, + "rng": 4253087318999719449, + "result": "success" + } + + The idea is for a game to start with a near future blockhash, so the lobby gets players signed up until just prior to the designated height. then that blockhash can be used to create a stream of rngs. + + the same initial rng can be used for all players, if the identical starting condition is required. up to 255 different initial rng can be derived from a single blockhash. (Actually any number is possible, for simplicity rng rpc limits to 255). + + you will notice maxrngs and lastrng, the lastrng is the rng value that will happen after maxrng iterations of calling rngnext with the current rng. This allows making time based multiplayer games where all the nodes can validate all the other nodes rng, even without realtime synchronization of all user input events. + + Every time period, all players would set their rng value to the lastrng value. The only thing to be careful of is it not exceed the maxrng calls to rngnext in a single time period. otherwise the same set of rng numbers will be repeated. + + events rpc is called after each user event and broadcasts to the network the tuple (gametxid, pk, eventid, payload). This allows the network to verify realtime user events from a specific pk/gametxid and also to make sure no user events were changed. In the event the events were changed, then the guilty pk will be blacklisted. If no realtime events, then the guilty pk would be penalized. + + ./c cclib events 17 \"[%226c%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,0]\" + ./c cclib events 17 \"[%226d%22,%229433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775%22,1]\" +*/ + + +CScript games_newgameopret(int64_t buyin,int32_t maxplayers) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'G' << buyin << maxplayers); + return(opret); +} + +uint8_t games_newgameopreturndecode(int64_t &buyin,int32_t &maxplayers,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> buyin; ss >> maxplayers) != 0 && e == EVAL_GAMES && f == 'G' ) + { + return(f); + } + return(0); +} + +CScript games_registeropret(uint256 gametxid,uint256 playertxid) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; + //fprintf(stderr,"opret.(%s %s).R\n",gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << gametxid << playertxid); + return(opret); +} + +CScript games_keystrokesopret(uint256 gametxid,uint256 batontxid,CPubKey pk,std::vectorkeystrokes) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'K' << gametxid << batontxid << pk << keystrokes); + return(opret); +} + +uint8_t games_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey &pk,std::vector &keystrokes,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> batontxid; ss >> pk; ss >> keystrokes) != 0 && e == EVAL_GAMES && f == 'K' ) + { + return(f); + } + return(0); +} + +uint8_t games_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey) +{ + std::string name, description; std::vector vorigPubkey; + std::vector> oprets; + std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + uint8_t e, f,*script; std::vector voutPubkeys; + tokenid = zeroid; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) + { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + vopret = vopretNonfungible; + } + else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 ) + { + GetOpretBlob(oprets, OPRETID_ROGUEGAMEDATA, vopretDummy); // blob from non-creation tx opret + vopret = vopretDummy; + } + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_GAMES && f == 'R' ) + { + return(f); + } + //fprintf(stderr,"e.%d f.%c game.%s playertxid.%s\n",e,f,gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + return(0); +} + +CScript games_finishopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CPubKey pk,std::vectorplayerdata,std::string pname) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; std::string symbol(ASSETCHAINS_SYMBOL); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << gametxid << symbol << pname << regslot << pk << playerdata ); + return(opret); +} + +uint8_t games_finishopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey) +{ + std::string name, description; std::vector vorigPubkey; + std::vector> oprets, opretsDummy; + std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + uint8_t e, f,*script; std::vector voutPubkeys; + tokenid = zeroid; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) + { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + vopret = vopretNonfungible; + } + else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 ) + { + //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); + GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx + vopret = vopretNonfungible; + } + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_GAMES && (f == 'H' || f == 'Q') ) + { + return(f); + } + fprintf(stderr,"SKIP obsolete: e.%d f.%c game.%s regslot.%d psize.%d\n",e,f,gametxid.GetHex().c_str(),regslot,(int32_t)playerdata.size()); + return(0); +} + +CScript games_eventopret(uint32_t timestamp,CPubKey pk,std::vector sig,std::vector payload) +{ + CScript opret; uint8_t evalcode = EVAL_GAMES; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'E' << timestamp << pk << sig << payload); + return(opret); +} + +uint8_t games_eventdecode(uint32_t ×tamp,CPubKey &pk,std::vector &sig,std::vector &payload,std::vector vopret) +{ + uint8_t e,f; + timestamp = 0; + if ( vopret.size() > 6 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> timestamp; ss >> pk; ss >> sig; ss >> payload) != 0 && e == EVAL_GAMES ) + { + return(f); + } + fprintf(stderr,"ERROR e.%d f.%d pk.%d sig.%d payload.%d\n",e,f,(int32_t)pk.size(),(int32_t)sig.size(),(int32_t)payload.size()); + return(0); +} + +UniValue games_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t n; uint64_t seed; + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 1 ) + { + seed = jdouble(jitem(params,0),0); + result.push_back(Pair("seed",seed)); + seed = _games_rngnext(seed); + result.push_back(Pair("rng",seed)); + result.push_back(Pair("result","success")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough params")); + } + return(result); +} + +UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t i,n,playerid=0; uint64_t seed=0,initseed; bits256 hash; + if ( params != 0 && ((n= cJSON_GetArraySize(params)) == 1 || n == 2) ) + { + hash = jbits256(jitem(params,0),0); + if ( n == 2 ) + { + playerid = juint(jitem(params,1),0); + if ( playerid >= 0xff ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","playerid too big")); + return(result); + } + } + playerid++; + for (i=0; i<8; i++) + { + if ( ((1 << i) & playerid) != 0 ) + seed ^= (uint64_t)hash.uints[i] << ((i&1)*32); + } + initseed = seed; + seed = _games_rngnext(initseed); + result.push_back(Pair("playerid",(int64_t)(playerid - 1))); + result.push_back(Pair("seed",initseed)); + result.push_back(Pair("rng",seed)); + for (i=0; i &sig,std::vector payload,CPubKey pk) +{ + static secp256k1_context *ctx; + size_t siglen = 74; secp256k1_pubkey pubkey; secp256k1_ecdsa_signature signature; int32_t len,verifyflag = 1; uint8_t privkey[32]; uint256 hash; uint32_t t; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( ctx != 0 ) + { + Myprivkey(privkey); + len = payload.size(); + payload.resize(len + 4); + if ( timestamp == 0 ) + { + timestamp = (uint32_t)time(NULL); + verifyflag = 0; + } + t = timestamp; + payload[len++] = t, t >>= 8; + payload[len++] = t, t >>= 8; + payload[len++] = t, t >>= 8; + payload[len++] = t; + vcalc_sha256(0,(uint8_t *)&hash,&payload[0],len); + if ( verifyflag == 0 ) + { + if ( secp256k1_ecdsa_sign(ctx,&signature,(uint8_t *)&hash,privkey,NULL,NULL) > 0 ) + { + sig.resize(siglen); + if ( secp256k1_ecdsa_signature_serialize_der(ctx,&sig[0],&siglen,&signature) > 0 ) + { + if ( siglen != sig.size() ) + sig.resize(siglen); + return(0); + } + else return(-3); + } else return(-2); + } + else + { + if ( secp256k1_ec_pubkey_parse(ctx,&pubkey,pk.begin(),33) > 0 ) + { + if ( secp256k1_ecdsa_signature_parse_der(ctx,&signature,&sig[0],sig.size()) > 0 ) + { + if ( secp256k1_ecdsa_verify(ctx,&signature,(uint8_t *)&hash,&pubkey) > 0 ) + return(0); + else return(-4); + } else return(-3); + } else return(-2); + } + } else return(-1); +} + +int32_t games_event(uint32_t timestamp,uint256 gametxid,int32_t eventid,std::vector payload) +{ + std::vector sig,vopret; CPubKey mypk; uint32_t x; int32_t i,len = payload.size(); + payload.resize(len + 4 + 32); + for (i=0; i<32; i++) + payload[len++] = ((uint8_t *)&gametxid)[i]; + x = eventid; + payload[len++] = x, x >>= 8; + payload[len++] = x, x >>= 8; + payload[len++] = x, x >>= 8; + payload[len++] = x; + mypk = pubkey2pk(Mypubkey()); + if ( games_eventsign(timestamp,sig,payload,mypk) == 0 ) + { + GetOpReturnData(games_eventopret(timestamp,mypk,sig,payload),vopret); + games_payloadrecv(mypk,timestamp,payload); + komodo_sendmessage(4,8,"events",vopret); + return(0); + } + fprintf(stderr,"games_eventsign error\n"); + return(-1); +} + +UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static uint256 lastgametxid; static uint32_t numevents; + UniValue result(UniValue::VOBJ); std::vector payload; int32_t len,i,n; uint32_t x; CPubKey mypk; char str[67]; uint32_t eventid,timestamp = 0; uint256 gametxid; + if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 1 && n <= 3 ) + { + mypk = pubkey2pk(Mypubkey()); + if ( payments_parsehexdata(payload,jitem(params,0),0) == 0 ) + { + if ( n >= 2 ) + gametxid = juint256(jitem(params,1)); + else gametxid = zeroid; + if ( gametxid != lastgametxid ) + { + lastgametxid = gametxid; + numevents = 1; + eventid = 0; + } + if ( n == 3 ) + { + eventid = juint(jitem(params,2),0); + if ( numevents <= eventid ) + numevents = eventid+1; + } else eventid = numevents++; + if ( games_event(timestamp,gametxid,eventid,payload) == 0 ) + { + result.push_back(Pair("gametxid",gametxid.GetHex())); + result.push_back(Pair("eventid",(int64_t)eventid)); + result.push_back(Pair("payload",jstr(jitem(params,0),0))); + result.push_back(Pair("timestamp",(int64_t)timestamp)); + result.push_back(Pair("result","success")); + result.push_back(Pair("pubkey33",pubkey33_str(str,(uint8_t *)&mypk))); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","signing ereror")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parsehexdata")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough params")); + } + return(result); +} + +void komodo_netevent(std::vector message) +{ + int32_t i,retval,lag,lagerr=0; uint32_t timestamp,now; CPubKey pk; std::vector sig,payload; char str[67]; + if ( games_eventdecode(timestamp,pk,sig,payload,message) == 'E' ) + { + now = (uint32_t)time(NULL); + lag = now - timestamp; + if ( lag < -3 || lag > 3 ) + { + fprintf(stderr,"LAG ERROR "); + lagerr = lag; + } + if ( (retval= games_eventsign(timestamp,sig,payload,pk)) != 0 ) + fprintf(stderr,"SIG ERROR.%d ",retval); + else if ( lagerr == 0 ) + { + if ( games_payloadrecv(pk,timestamp,payload) == 0 ) // first time this is seen + { + if ( (rand() % 10) == 0 ) + { + //fprintf(stderr,"relay message.[%d]\n",(int32_t)message.size()); + komodo_sendmessage(2,2,"events",message); + } + } + } + //for (i=0; i 0 ) + result.push_back(Pair("maxplayers",maxplayers)); + if ( buyin >= 0 ) + { + result.push_back(Pair("buyin",ValueFromAmount(buyin))); + if ( buyin == 0 ) + result.push_back(Pair("type","newbie")); + else result.push_back(Pair("type","buyin")); + } +} + +int32_t games_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0) +{ + uint256 hashBlock; int32_t i,numvouts; char coinaddr[64]; CPubKey gamespk; uint64_t txfee = 10000; + buyin = maxplayers = 0; + if ( (txid == zeroid || myGetTransaction(txid,tx,hashBlock) != 0) && (numvouts= tx.vout.size()) > 1 ) + { + if ( txid != zeroid ) + gameheight = komodo_blockheight(hashBlock); + else + { + txid = tx.GetHash(); + //fprintf(stderr,"set txid %s %llu\n",txid.GetHex().c_str(),(long long)CCgettxout(txid,0,1)); + } + if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) == txfee && (unspentv0 == 0 || CCgettxout(txid,0,1,0) == txfee) ) + { + if ( games_newgameopreturndecode(buyin,maxplayers,tx.vout[numvouts-1].scriptPubKey) == 'G' ) + { + if ( maxplayers < 1 || maxplayers > GAMES_MAXPLAYERS || buyin < 0 ) + return(-6); + if ( numvouts > 2*maxplayers+1 ) + { + for (i=0; i= 0 ) + txid = spenttxid; + else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + break; + } + txid = spenttxid; + vout = 0; + //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + if ( spentvini != 0 ) + break; + if ( n++ > GAMES_MAXITERATIONS ) + break; + } + if ( txid != zeroid ) + { + if ( myGetTransaction(txid,tx,hashBlock) != 0 ) + { + if ( (pindex= komodo_blockindex(hashBlock)) != 0 ) + { + if ( pindex->GetHeight() <= gameht+GAMES_MAXKEYSTROKESGAP ) + alive++; + } + } + } + } + } + else if ( registration_open != 0 ) + openslots++; + } + //fprintf(stderr,"numalive.%d openslots.%d\n",alive,openslots); + return(alive); +} + +UniValue games_playerobj(std::vector playerdata,uint256 playertxid,uint256 tokenid,std::string symbol,std::string pname,uint256 gametxid) +{ + int32_t i,vout,spentvini,numvouts,n=0; uint256 txid,spenttxid,hashBlock; struct games_player P; char packitemstr[512],*datastr=0; UniValue obj(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx; + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + datastr = (char *)malloc(playerdata.size()*2+1); + for (i=0; i= 0 ) + txid = spenttxid; + else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + break; + } + txid = spenttxid; + vout = 0; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + for (i=0; i GAMES_MAXITERATIONS ) + break; + } + obj.push_back(Pair("gametxid",gametxid.GetHex())); + if ( txid != playertxid ) + obj.push_back(Pair("batontxid",txid.GetHex())); + obj.push_back(Pair("playertxid",playertxid.GetHex())); + if ( tokenid != zeroid ) + obj.push_back(Pair("tokenid",tokenid.GetHex())); + else obj.push_back(Pair("tokenid",playertxid.GetHex())); + if ( datastr != 0 ) + { + obj.push_back(Pair("data",datastr)); + free(datastr); + } + obj.push_back(Pair("pack",a)); + GAMEPLAYERJSON(obj,&P); + obj.push_back(Pair("chain",symbol)); + obj.push_back(Pair("pname",pname)); + return(obj); +} + +int32_t games_iterateplayer(uint256 ®istertxid,uint256 firsttxid,int32_t firstvout,uint256 lasttxid) // retrace playertxid vins to reach highlander <- this verifies player is valid and rogue_playerdataspend makes sure it can only be used once +{ + uint256 spenttxid,txid = firsttxid; int32_t spentvini,n,vout = firstvout; + registertxid = zeroid; + if ( vout < 0 ) + return(-1); + n = 0; + while ( (spentvini= myIsutxo_spent(spenttxid,txid,vout)) == 0 ) + { + txid = spenttxid; + vout = spentvini; + if ( registertxid == zeroid ) + registertxid = txid; + if ( ++n >= GAMES_MAXITERATIONS ) + { + fprintf(stderr,"games_iterateplayer n.%d, seems something is wrong\n",n); + return(-2); + } + } + if ( txid == lasttxid ) + return(0); + else + { + fprintf(stderr,"firsttxid.%s/v%d -> %s != last.%s\n",firsttxid.ToString().c_str(),firstvout,txid.ToString().c_str(),lasttxid.ToString().c_str()); + return(-1); + } +} + +int32_t games_playerdata(struct CCcontract_info *cp,uint256 &origplayergame,uint256 &tokenid,CPubKey &pk,std::vector &playerdata,std::string &symbol,std::string &pname,uint256 playertxid) +{ + uint256 origplayertxid,hashBlock,gametxid,registertxid; CTransaction gametx,playertx,highlandertx; std::vector vopret; uint8_t *script,e,f; int32_t i,regslot,gameheight,numvouts,maxplayers; int64_t buyin; + if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 && (numvouts= playertx.vout.size()) > 0 ) + { + if ( (f= games_finishopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,playertx.vout[numvouts-1].scriptPubKey)) == 'H' || f == 'Q' ) + { + origplayergame = gametxid; + if ( tokenid != zeroid ) + { + playertxid = tokenid; + if ( myGetTransaction(playertxid,playertx,hashBlock) == 0 || (numvouts= playertx.vout.size()) <= 0 ) + { + fprintf(stderr,"couldnt get tokenid.%s\n",playertxid.GetHex().c_str()); + return(-2); + } + } + if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) + { + //fprintf(stderr,"playertxid.%s got vin.%s/v%d gametxid.%s iterate.%d\n",playertxid.ToString().c_str(),playertx.vin[1].prevout.hash.ToString().c_str(),(int32_t)playertx.vin[1].prevout.n-maxplayers,gametxid.ToString().c_str(),games_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid)); + if ( (tokenid != zeroid || playertx.vin[1].prevout.hash == gametxid) && games_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid) == 0 ) + { + // if registertxid has vin from pk, it can be used + return(0); + } else fprintf(stderr,"hash mismatch or illegal gametxid\n"); + } else fprintf(stderr,"invalid game %s\n",gametxid.GetHex().c_str()); + } //else fprintf(stderr,"invalid player funcid.%c\n",f); + } else fprintf(stderr,"couldnt get playertxid.%s\n",playertxid.GetHex().c_str()); + return(-1); +} + +int32_t games_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx,char *mygamesaddr) +{ + int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; char destaddr[64]; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); + if ( strcmp(mygamesaddr,destaddr) == 0 ) + return(1); + //else fprintf(stderr,"myaddr.%s vs %s\n",mygamesaddr,destaddr); + } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"vout %d is unspent\n",vout); + } + return(0); +} + +int64_t games_buyins(uint256 gametxid,int32_t maxplayers) +{ + int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; int64_t buyins = 0; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + if ( spenttx.vout[0].nValue > GAMES_REGISTRATIONSIZE ) + buyins += (spenttx.vout[0].nValue - GAMES_REGISTRATIONSIZE); + } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"vout %d is unspent\n",vout); + } + return(buyins); +} + +uint64_t games_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *mygamesaddr) +{ + CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; + if ( myGetTransaction(gametxid,tx,hashBlock) != 0 && (pindex= komodo_blockindex(hashBlock)) != 0 ) + { + ht = pindex->GetHeight(); + delay = GAMES_REGISTRATION * (maxplayers > 1); + obj.push_back(Pair("height",ht)); + obj.push_back(Pair("start",ht+delay)); + if ( komodo_nextheight() > ht+delay ) + { + if ( (pindex= komodo_chainactive(ht+delay)) != 0 ) + { + hashBlock = pindex->GetBlockHash(); + obj.push_back(Pair("starthash",hashBlock.ToString())); + memcpy(&seed,&hashBlock,sizeof(seed)); + seed &= (1LL << 62) - 1; + obj.push_back(Pair("seed",(int64_t)seed)); + if ( games_iamregistered(maxplayers,gametxid,tx,mygamesaddr) > 0 ) + sprintf(cmd,"cc/%s %llu %s",GAMENAME,(long long)seed,gametxid.ToString().c_str()); + else sprintf(cmd,"./komodo-cli -ac_name=%s cclib register %d \"[%%22%s%%22]\"",ASSETCHAINS_SYMBOL,EVAL_GAMES,gametxid.ToString().c_str()); + obj.push_back(Pair("run",cmd)); + } + } + obj.push_back(Pair("alive",games_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx))); + obj.push_back(Pair("openslots",openslots)); + obj.push_back(Pair("numplayers",numplayers)); + obj.push_back(Pair("buyins",ValueFromAmount(games_buyins(gametxid,maxplayers)))); + } + obj.push_back(Pair("maxplayers",maxplayers)); + obj.push_back(Pair("buyin",ValueFromAmount(buyin))); + return(seed); +} + +UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); std::vector playerdata; uint256 playertxid,tokenid,origplayergame;int32_t n; CPubKey pk; bits256 t; std::string symbol,pname; + result.push_back(Pair("result","success")); + games_univalue(result,"playerinfo",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + playertxid = juint256(jitem(params,0)); + if ( games_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) + return(cclib_error(result,"invalid playerdata")); + result.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,origplayergame))); + } else return(cclib_error(result,"no playertxid")); + return(result); + } else return(cclib_error(result,"couldnt reparse params")); +} + +void disp_gamesplayerdata(std::vector playerdata) +{ + struct games_player P; int32_t i; char packitemstr[512],str[512]; + if ( playerdata.size() > 0 ) + { + for (i=0; i maxplayers+1 ) + { + r = rand() % maxplayers; + for (j=0; j &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname) +{ + int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64]; gamesevent *keystrokes=0; + batonvalue = numkeys = numplayers = batonht = 0; + playertxid = batontxid = zeroid; + if ( keystrokesp != 0 ) + *keystrokesp = 0; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + numplayers++; + Getscriptaddress(ccaddr,spenttx.vout[0].scriptPubKey); + if ( strcmp(destaddr,ccaddr) == 0 ) + { + matches++; + regslot = i; + matchtx = spenttx; + } //else fprintf(stderr,"%d+1 doesnt match %s vs %s\n",i,ccaddr,destaddr); + } //else fprintf(stderr,"%d+1 couldnt find spenttx.%s\n",i,spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"%d+1 unspent\n",i); + } + if ( matches == 1 ) + { + numvouts = matchtx.vout.size(); +//fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); + if ( games_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid ) + { + //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + if ( tokenid != zeroid ) + active = tokenid; + else active = playertxid; + if ( active == zeroid || games_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 ) + { + txid = matchtx.GetHash(); + //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); + n = 0; + while ( CCgettxout(txid,0,1,0) < 0 ) + { + spenttxid = zeroid; + spentvini = -1; + if ( (spentvini= myIsutxo_spent(spenttxid,txid,0)) >= 0 ) + txid = spenttxid; + else + { + if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,0) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + return(-2); + } + } + txid = spenttxid; + //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + if ( spentvini != 0 ) // game is over? + { + //fprintf(stderr,"gameisover n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + return(0); + } + if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) + { + uint256 g,b; CPubKey p; std::vector k; + if ( games_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) + { + //fprintf(stderr,"update keystrokes.%p[%d]\n",keystrokes,numkeys); + keystrokes = (gamesevent *)realloc(keystrokes,(int32_t)(sizeof(*keystrokes)*numkeys + k.size())); + for (i=0; i= GAMES_MAXITERATIONS ) + { + fprintf(stderr,"games_findbaton n.%d, seems something is wrong\n",n); + return(-5); + } + } + //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); + batontxid = txid; + batonvout = 0; // not vini + // how to detect timeout, bailedout, highlander + hashBlock = zeroid; + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 0 ) + { + if ( hashBlock == zeroid ) + batonht = komodo_nextheight(); + else if ( (pindex= komodo_blockindex(hashBlock)) == 0 ) + return(-4); + else batonht = pindex->GetHeight(); + batonvalue = batontx.vout[0].nValue; + //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); + return(0); + } else fprintf(stderr,"couldnt find baton\n"); + } else fprintf(stderr,"error with playerdata\n"); + } else fprintf(stderr,"findbaton opret error\n"); + } + return(-1); +} + +void games_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *mygamesaddr) +{ + // identify if bailout or quit or timed out + uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname; + destaddr[0] = 0; + if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); + } + obj.push_back(Pair("slot",(int64_t)vout-1)); + if ( (retval= games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 ) + { + if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 ) + { + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 ) + { + if ( games_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid ) + obj.push_back(Pair("status","registered")); + else obj.push_back(Pair("status","alive")); + } else obj.push_back(Pair("status","error")); + } else obj.push_back(Pair("status","finished")); + obj.push_back(Pair("baton",batontxid.ToString())); + obj.push_back(Pair("tokenid",tokenid.ToString())); + obj.push_back(Pair("batonaddr",destaddr)); + obj.push_back(Pair("ismine",strcmp(mygamesaddr,destaddr)==0)); + obj.push_back(Pair("batonvout",(int64_t)batonvout)); + obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue))); + obj.push_back(Pair("batonht",(int64_t)batonht)); + if ( playerdata.size() > 0 ) + obj.push_back(Pair("player",games_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid))); + } else fprintf(stderr,"findbaton err.%d\n",retval); +} + +UniValue games_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey gamespk,mypk; char *jsonstr; uint64_t inputsum,change,required,buyin=0; int32_t i,n,maxplayers = 1; + if ( txfee == 0 ) + txfee = 10000; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + maxplayers = juint(jitem(params,0),0); + if ( n > 1 ) + buyin = jdouble(jitem(params,1),0) * COIN + 0.0000000049; + } + } + if ( maxplayers < 1 || maxplayers > GAMES_MAXPLAYERS ) + return(cclib_error(result,"illegal maxplayers")); + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + games_univalue(result,"newgame",maxplayers,buyin); + required = (3*txfee + maxplayers*(GAMES_REGISTRATIONSIZE+txfee)); + if ( (inputsum= AddCClibInputs(cp,mtx,gamespk,required,16,cp->unspendableCCaddr,1)) >= required ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gamespk)); + for (i=0; ievalcode,GAMES_REGISTRATIONSIZE,gamespk,gamespk)); + for (i=0; ievalcode,txfee,gamespk,gamespk)); + if ( (change= inputsum - required) >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,gamespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_newgameopret(buyin,maxplayers)); + return(games_rawtxresult(result,rawtx,1)); + } + else return(cclib_error(result,"illegal maxplayers")); + return(result); +} + +UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 txid,hashBlock; CTransaction tx; int32_t openslots,maxplayers,numplayers,gameheight,nextheight,vout,numvouts; CPubKey gamespk; char coinaddr[64]; + std::vector > unspentOutputs; + gamespk = GetUnspendable(cp,0); + GetCCaddress(cp,coinaddr,gamespk); + SetCCunspents(unspentOutputs,coinaddr,true); + nextheight = komodo_nextheight(); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( it->second.satoshis != txfee || vout != 0 ) // reject any that are not highlander markers + continue; + if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,1) == 0 && nextheight <= gameheight+GAMES_MAXKEYSTROKESGAP ) + { + games_playersalive(openslots,numplayers,txid,maxplayers,gameheight,tx); + if ( openslots > 0 ) + a.push_back(txid.GetHex()); + } + } + result.push_back(Pair("result","success")); + games_univalue(result,"pending",-1,-1); + result.push_back(Pair("pending",a)); + result.push_back(Pair("numpending",(int64_t)a.size())); + return(result); +} + +UniValue games_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char myaddr[64],str[64]; CPubKey mypk,gamespk; + result.push_back(Pair("name","games")); + result.push_back(Pair("method","info")); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + txid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",txid.GetHex())); + if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,0) == 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("gameheight",(int64_t)gameheight)); + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,myaddr,gamespk,mypk); + seed = games_gamefields(result,maxplayers,buyin,txid,myaddr); + result.push_back(Pair("seed",(int64_t)seed)); + for (i=0; i GAMES_REGISTRATIONSIZE 1of2 registration baton from creategame + // vin1 -> optional nonfungible character vout @ + // vin2 -> original creation TCBOO playerdata used + // vin3+ -> buyin + // vout0 -> keystrokes/completion baton + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 tokenid,gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts,vout=1; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,gamespk,burnpk; CTransaction tx,playertx; std::vector playerdata; std::string rawtx,symbol,pname; bits256 t; + + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + gamespk = GetUnspendable(cp,0); + games_univalue(result,"register",-1,-1); + playertxid = tokenid = zeroid; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + if ( (err= games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1)) == 0 ) + { + if ( n > 1 ) + { + playertxid = juint256(jitem(params,1)); + if ( games_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) + return(cclib_error(result,"couldnt extract valid playerdata")); + if ( tokenid != zeroid ) // if it is tokentransfer this will be 0 + vout = 1; + } + if ( komodo_nextheight() > gameheight + GAMES_MAXKEYSTROKESGAP ) + return(cclib_error(result,"didnt register in time, GAMES_MAXKEYSTROKESGAP")); + games_univalue(result,0,maxplayers,buyin); + GetCCaddress1of2(cp,coinaddr,gamespk,mypk); + if ( games_iamregistered(maxplayers,gametxid,tx,coinaddr) > 0 ) + return(cclib_error(result,"already registered")); + if ( (inputsum= games_registrationbaton(mtx,gametxid,tx,maxplayers)) != GAMES_REGISTRATIONSIZE ) + return(cclib_error(result,"couldnt find available registration baton")); + else if ( playertxid != zeroid && games_playerdataspend(mtx,playertxid,vout,origplayergame) < 0 ) + return(cclib_error(result,"couldnt find playerdata to spend")); + else if ( buyin > 0 && AddNormalinputs(mtx,mypk,buyin,64) < buyin ) + return(cclib_error(result,"couldnt find enough normal funds for buyin")); + if ( tokenid != zeroid ) + { + mtx.vin.push_back(CTxIn(tokenid,0)); // spending cc marker as token is burned + char unspendableTokenAddr[64]; uint8_t tokenpriv[32]; struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + CPubKey unspPk = GetUnspendable(cpTokens, tokenpriv); + GetCCaddress(cpTokens, unspendableTokenAddr, unspPk); + CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); + } + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,buyin + inputsum - txfee,gamespk,mypk)); + GetCCaddress1of2(cp,destaddr,gamespk,gamespk); + CCaddr1of2set(cp,gamespk,gamespk,cp->CCpriv,destaddr); + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); + + uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; + CScript opretRegister = games_registeropret(gametxid, playertxid); + if ( playertxid != zeroid ) + { + voutPubkeysEmpty.push_back(burnpk); + if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 ) + { + std::vector> oprets; + if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0) + { // if token in the opret + didtx = 1; + if ( funcid == 'c' ) + tid = tokenid == zeroid ? playertxid : tokenid; + vscript_t vopretRegister; + GetOpReturnData(opretRegister, vopretRegister); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, + EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_ROGUEGAMEDATA, vopretRegister))); + } + } + } + if ( didtx == 0 ) + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opretRegister); + + return(games_rawtxresult(result,rawtx,1)); + } else return(cclib_error(result,"invalid gametxid")); + } else return(cclib_error(result,"no gametxid")); + } else return(cclib_error(result,"couldnt reparse params")); +} + +UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + // vin0 -> baton from registration or previous keystrokes + // vout0 -> new baton + // opret -> user input chars + // being killed should auto broadcast (possible to be suppressed?) + // respawn to be prevented by including timestamps + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); + UniValue result(UniValue::VOBJ); CPubKey gamespk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32]; + // if ( txfee == 0 ) + txfee = 1000; // smaller than normal on purpose + games_univalue(result,"keystrokes",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + result.push_back(Pair("keystrokes",keystrokestr)); + keystrokes = ParseHex(keystrokestr); + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,destaddr,gamespk,mypk); + if ( games_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1) == 0 ) + { + if ( games_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,tx,maxplayers,destaddr,numplayers,symbol,pname) == 0 ) + { + result.push_back(Pair("batontxid",batontxid.GetHex())); + result.push_back(Pair("playertxid",playertxid.GetHex())); + if ( maxplayers == 1 || nextheight <= batonht+GAMES_MAXKEYSTROKESGAP ) + { + mtx.vin.push_back(CTxIn(batontxid,batonvout,CScript())); //this validates user if pk + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,batonvalue-txfee,gamespk,mypk)); + Myprivkey(mypriv); + CCaddr1of2set(cp,gamespk,mypk,mypriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,games_keystrokesopret(gametxid,batontxid,mypk,keystrokes)); + //fprintf(stderr,"KEYSTROKES.(%s)\n",rawtx.c_str()); + return(games_rawtxresult(result,rawtx,1)); + } else return(cclib_error(result,"keystrokes tx was too late")); + } else return(cclib_error(result,"couldnt find batontxid")); + } else return(cclib_error(result,"invalid gametxid")); + } else return(cclib_error(result,"couldnt reparse params")); +} + +gamesevent *games_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *gamesaddr) +{ + CPubKey gamespk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64]; gamesevent *keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct games_player P,endP; + gamespk = GetUnspendable(cp,0); + *numkeysp = 0; + seed = 0; + num = numkeys = 0; + playertxid = zeroid; + str[0] = 0; + if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) + { + if ( (retval= games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,gamesaddr,numplayers,symbol,pname)) == 0 ) + { + UniValue obj; + //fprintf(stderr,"got baton\n"); + seed = games_gamefields(obj,maxplayers,buyin,gametxid,gamesaddr); + //fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i no playerdata\n"); + newdata.resize(0); + *numkeysp = numkeys; + return(keystrokes); + } + else + { + *numkeysp = numkeys; + return(keystrokes); + } + } else num = 0; + } + else + { + fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); + if ( keystrokes != 0 ) + free(keystrokes), keystrokes = 0; + } + } else fprintf(stderr,"extractgame: invalid game\n"); + //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); + return(0); +} + +UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); CPubKey pk,gamespk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],gamesaddr[64],*pubstr,*hexstr; gamesevent *keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; + pk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + result.push_back(Pair("name","games")); + result.push_back(Pair("method","extract")); + gamesaddr[0] = 0; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + if ( n == 2 ) + { + if ( (pubstr= jstr(jitem(params,1),0)) != 0 ) + { + if (strlen(pubstr) == 66 ) + { + decode_hex(pub33,33,pubstr); + pk = buf2pk(pub33); + } + else if ( strlen(pubstr) < 36 ) + strcpy(gamesaddr,pubstr); + } + //fprintf(stderr,"gametxid.%s %s\n",gametxid.GetHex().c_str(),pubstr); + } + if ( gamesaddr[0] == 0 ) + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + result.push_back(Pair("gamesaddr",gamesaddr)); + str[0] = 0; + if ( (keystrokes= games_extractgame(1,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) + { + result.push_back(Pair("status","success")); + flag = 1; + hexstr = (char *)calloc(1,sizeof(gamesevent)*numkeys*2 + 1); + for (i=0; i highlander vout from creategame TCBOO + //vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than 60 blocks since last keystrokes, it is forfeit + //vins2+ -> rest of unspent registration utxo so all newgame vouts are spent + //vout0 -> nonfungible character with pack @ + //vout1 -> 1% ingame gold and all the buyins + + // detect if last to bailout + // vin0 -> kestrokes baton of completed game with Q + // vout0 -> playerdata marker + // vout0 -> 1% ingame gold + // get any playerdata, get all keystrokes, replay game and compare final state + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,numkeys,maxplayers,batonht,batonvout; char mygamesaddr[64],str[512]; gamesevent *keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,gamespk; uint8_t player[10000],mypriv[32],funcid; + struct CCcontract_info *cpTokens, tokensC; + + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,mygamesaddr,gamespk,mypk); + result.push_back(Pair("name","games")); + result.push_back(Pair("method",method)); + result.push_back(Pair("mygamesaddr",mygamesaddr)); + if ( strcmp(method,"bailout") == 0 ) + funcid = 'Q'; + else funcid = 'H'; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + if ( (err= games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,1)) == 0 ) + { + if ( games_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,mygamesaddr,numplayers,symbol,pname) == 0 ) + { + UniValue obj; struct games_player P; + seed = games_gamefields(obj,maxplayers,buyin,gametxid,mygamesaddr); + fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d\n",pname.size()!=0?pname.c_str():Games_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i 0 ) + { + newdata.resize(num); + for (i=0; i no playerdata\n"); + newdata.resize(0); + } + else + { + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk)); + cashout = games_cashout(&P); + fprintf(stderr,"\ncashout %.8f extracted %s\n",(double)cashout/COIN,str); + if ( funcid == 'H' && maxplayers > 1 ) + { + /*if ( P.amulet == 0 ) + { + if ( numplayers != maxplayers ) + return(cclib_error(result,"numplayers != maxplayers")); + else if ( games_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) + return(cclib_error(result,"highlander must be a winner or last one standing")); + }*/ + cashout += games_buyins(gametxid,maxplayers);//numplayers * buyin; + } + if ( cashout > 0 ) + { + if ( (inputsum= AddCClibInputs(cp,mtx,gamespk,cashout,60,cp->unspendableCCaddr,1)) > cashout ) + CCchange = (inputsum - cashout); + else fprintf(stderr,"couldnt find enough utxos\n"); + } + mtx.vout.push_back(CTxOut(cashout,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + } + } + if ( CCchange + (batonvalue-3*txfee) >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),gamespk)); + Myprivkey(mypriv); + CCaddr1of2set(cp,gamespk,mypk,mypriv,mygamesaddr); + CScript opret; + if ( pname.size() == 0 ) + pname = Games_pname; + if ( newdata.size() == 0 ) + { + opret = games_finishopret(funcid, gametxid, regslot, mypk, nodata,pname); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,opret); + //fprintf(stderr,"nodata finalizetx.(%s)\n",rawtx.c_str()); + } + else + { + opret = games_finishopret(funcid, gametxid, regslot, mypk, newdata,pname); + char seedstr[32]; + sprintf(seedstr,"%llu",(long long)seed); + std::vector vopretNonfungible; + GetOpReturnData(opret, vopretNonfungible); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vopretNonfungible)); + } + return(games_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","success")); + } else fprintf(stderr,"illegal game err.%d\n",err); + } else fprintf(stderr,"parameters only n.%d\n",n); + } + return(result); +} + +UniValue games_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + return(games_finish(txfee,cp,params,(char *)"bailout")); +} + +UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + return(games_finish(txfee,cp,params,(char *)"highlander")); +} + +UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 tokenid,gametxid,txid,hashBlock; CTransaction playertx,tx; int32_t maxplayers,vout,numvouts; std::vector playerdata; CPubKey gamespk,mypk,pk; std::string symbol,pname; char coinaddr[64]; + std::vector > unspentOutputs; + gamespk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + GetTokensCCaddress(cp,coinaddr,mypk); + SetCCunspents(unspentOutputs,coinaddr,true); + games_univalue(result,"players",-1,-1); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( it->second.satoshis != 1 || vout > 1 ) + continue; + if ( games_playerdata(cp,gametxid,tokenid,pk,playerdata,symbol,pname,txid) == 0 )//&& pk == mypk ) + { + a.push_back(txid.GetHex()); + //a.push_back(Pair("playerdata",games_playerobj(playerdata))); + } + } + result.push_back(Pair("playerdata",a)); + result.push_back(Pair("numplayerdata",(int64_t)a.size())); + return(result); +} + +UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey gamespk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; + std::vector > addressIndex; + gamespk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + GetCCaddress1of2(cp,coinaddr,gamespk,mypk); + SetCCtxids(addressIndex,coinaddr,true); + games_univalue(result,"games",-1,-1); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( vout == 0 ) + { + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( games_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' ) + { + if ( games_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) + { + if ( CCgettxout(txid,vout,1,0) < 0 ) + b.push_back(gametxid.GetHex()); + else a.push_back(gametxid.GetHex()); + } + } + } + } + } + result.push_back(Pair("pastgames",b)); + result.push_back(Pair("games",a)); + result.push_back(Pair("numgames",(int64_t)(a.size()+b.size()))); + return(result); +} + +UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t n; char *namestr = 0; + games_univalue(result,"setname",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + if ( (namestr= jstri(params,0)) != 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("pname",namestr)); + Games_pname = namestr; + return(result); + } + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt get name")); + return(result); +} + +UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; int64_t amount,inputsum; CPubKey gamespk,mypk; CScript opret; + if ( params != 0 && cJSON_GetArraySize(params) == 1 ) + { + amount = jdouble(jitem(params,0),0) * COIN + 0.0000000049; + gamespk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + if ( amount > GAMES_TXFEE ) + { + if ( (inputsum= AddNormalinputs(mtx,mypk,amount+GAMES_TXFEE,64)) >= amount+GAMES_TXFEE ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,gamespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,GAMES_TXFEE,opret); + return(games_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","amount too small")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse")); + } + return(result); +} + +int32_t games_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) +{ + static uint32_t good,bad; static uint256 prevgame; + char str[512],gamesaddr[64],str2[67],fname[64]; gamesevent *keystrokes; int32_t i,numkeys; std::vector newdata; uint64_t seed; CPubKey gamespk; struct games_player P; + *cashoutp = 0; + gamespk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,gamesaddr,gamespk,pk); + if ( (keystrokes= games_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,gamesaddr)) != 0 ) + { + free(keystrokes); + sprintf(fname,"%s.%llu.pack",GAMENAME,(long long)seed); + remove(fname); + for (i=0; i +#include +#define GAMES_RNGMULT 11109 +#define GAMES_RNGOFFSET 13849 +#define GAMES_MAXRNGS 10000 + +#ifndef STANDALONE + +#define ENABLE_WALLET +extern CWallet* pwalletMain; + +#include "CCinclude.h" +#include "secp256k1.h" + + +#define EVAL_GAMES (EVAL_FAUCET2+1) +#define GAMES_TXFEE 10000 +#define GAMES_MAXITERATIONS 777 +#define GAMES_MAXKEYSTROKESGAP 60 +#define GAMES_MAXPLAYERS 64 +#define GAMES_REGISTRATIONSIZE (100 * 10000) +#define GAMES_REGISTRATION 1 + + +#define MYCCNAME "games" + +std::string Games_pname; + +#define RPC_FUNCS \ + { (char *)MYCCNAME, (char *)"rng", (char *)"hash,playerid", 1, 2, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"rngnext", (char *)"seed", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"fund", (char *)"amount", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"players", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"games", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"pending", (char *)"no params", 0, 0, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"setname", (char *)"pname", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"newgame", (char *)"maxplayers,buyin", 2, 2, 'C', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"playerinfo", (char *)"playertxid", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"gameinfo", (char *)"gametxid", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"keystrokes", (char *)"txid,hexstr", 2, 2, 'K', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"bailout", (char *)"gametxid", 1, 1, 'Q', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"highlander", (char *)"gametxid", 1, 1, 'H', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"events", (char *)"eventshex [gametxid [eventid]]", 1, 3, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"extract", (char *)"gametxid [pubkey]", 1, 2, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"bet", (char *)"amount hexstr", 2, 2, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"settle", (char *)"height", 1, 1, ' ', EVAL_GAMES }, \ + { (char *)MYCCNAME, (char *)"register", (char *)"gametxid [playertxid]", 1, 2, 'R', EVAL_GAMES }, + +bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); +UniValue games_rng(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_rngnext(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_fund(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_events(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +#define CUSTOM_DISPATCH \ +if ( cp->evalcode == EVAL_GAMES ) \ +{ \ + if ( strcmp(method,"rng") == 0 ) \ + return(games_rng(txfee,cp,params)); \ + else if ( strcmp(method,"rngnext") == 0 ) \ + return(games_rngnext(txfee,cp,params)); \ + else if ( strcmp(method,"newgame") == 0 ) \ + return(games_newgame(txfee,cp,params)); \ + else if ( strcmp(method,"gameinfo") == 0 ) \ + return(games_gameinfo(txfee,cp,params)); \ + else if ( strcmp(method,"register") == 0 ) \ + return(games_register(txfee,cp,params)); \ + else if ( strcmp(method,"events") == 0 ) \ + return(games_events(txfee,cp,params)); \ + else if ( strcmp(method,"players") == 0 ) \ + return(games_players(txfee,cp,params)); \ + else if ( strcmp(method,"games") == 0 ) \ + return(games_games(txfee,cp,params)); \ + else if ( strcmp(method,"pending") == 0 ) \ + return(games_pending(txfee,cp,params)); \ + else if ( strcmp(method,"setname") == 0 ) \ + return(games_setname(txfee,cp,params)); \ + else if ( strcmp(method,"playerinfo") == 0 ) \ + return(games_playerinfo(txfee,cp,params)); \ + else if ( strcmp(method,"keystrokes") == 0 ) \ + return(games_keystrokes(txfee,cp,params)); \ + else if ( strcmp(method,"extract") == 0 ) \ + return(games_extract(txfee,cp,params)); \ + else if ( strcmp(method,"bailout") == 0 ) \ + return(games_bailout(txfee,cp,params)); \ + else if ( strcmp(method,"highlander") == 0 ) \ + return(games_highlander(txfee,cp,params)); \ + else if ( strcmp(method,"fund") == 0 ) \ + return(games_fund(txfee,cp,params)); \ + else if ( strcmp(method,"bet") == 0 ) \ + return(games_bet(txfee,cp,params)); \ + else if ( strcmp(method,"settle") == 0 ) \ + return(games_settle(txfee,cp,params)); \ + else \ + { \ + result.push_back(Pair("result","error")); \ + result.push_back(Pair("error","invalid gamescc method")); \ + return(result); \ + } \ +} +#endif + +#endif diff --git a/src/cc/gateways.cpp b/src/cc/gateways.cpp index c64d56b80..e15735d70 100644 --- a/src/cc/gateways.cpp +++ b/src/cc/gateways.cpp @@ -14,6 +14,7 @@ ******************************************************************************/ #include "CCGateways.h" +#include "key_io.h" /* prevent duplicate bindtxid via mempool scan @@ -145,162 +146,195 @@ ++ The gatewaysclaim automatically transfers the deposit amount of tokens to depositor's address (within EVAL_TOKENS). */ - - -void GatewaysAddQueue(std::string coin,uint256 txid,CScript scriptPubKey,int64_t nValue) -{ - char destaddr[64],str[65]; - Getscriptaddress(destaddr,scriptPubKey); - fprintf(stderr,"GatewaysAddQueue: %s %s %s %.8f\n",coin.c_str(),uint256_str(str,txid),destaddr,(double)nValue/COIN); -} - // start of consensus code -CScript EncodeGatewaysBindOpRet(uint8_t funcid,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2) +#define KMD_PUBTYPE 60 +#define KMD_P2SHTYPE 85 +#define KMD_WIFTYPE 188 +#define KMD_TADDR 0 +#define CC_MARKER_VALUE 10000 + +CScript EncodeGatewaysBindOpRet(uint8_t funcid,uint256 tokenid,std::string coin,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector gatewaypubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2,uint8_t wiftype) { - CScript opret; uint8_t evalcode = EVAL_GATEWAYS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << prefix << prefix2 << taddr << tokenid << totalsupply << M << N << pubkeys << oracletxid); - return(opret); + CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk; + std::vector pubkeys; + vscript_t vopret; + + cp = CCinit(&C,EVAL_GATEWAYS); + gatewayspk = GetUnspendable(cp,0); + pubkeys.push_back(gatewayspk); + vopret = E_MARSHAL(ss << evalcode << funcid << coin << totalsupply << oracletxid << M << N << gatewaypubkeys << taddr << prefix << prefix2 << wiftype); + return(EncodeTokenOpRet(tokenid,pubkeys, std::make_pair(OPRETID_GATEWAYSDATA, vopret))); } -uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,std::string &coin,uint256 &tokenid,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &pubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2) +uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype) { - std::vector vopret; uint8_t *script,e,f; - GetOpReturnData(scriptPubKey, vopret); + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); depositaddr[0] = 0; - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> prefix; ss >> prefix2; ss >> taddr; ss >> tokenid; ss >> totalsupply; ss >> M; ss >> N; ss >> pubkeys; ss >> oracletxid) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> totalsupply; ss >> oracletxid; ss >> M; ss >> N; ss >> gatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 ) { - if ( prefix == 60 ) + if ( prefix == KMD_PUBTYPE && prefix2 == KMD_P2SHTYPE ) { if ( N > 1 ) { - strcpy(depositaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,pubkeys))).ToString().c_str()); - //Getscriptaddress(depositaddr,GetScriptForMultisig(M,pubkeys)); - fprintf(stderr,"f.%c M.%d of N.%d size.%d -> %s\n",f,M,N,(int32_t)pubkeys.size(),depositaddr); - } else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(pubkeys[0])) << OP_CHECKSIG); + strcpy(depositaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,gatewaypubkeys))).ToString().c_str()); + LOGSTREAM("gatewayscc", CCLOG_DEBUG1, stream << "f." << f << " M." << (int)M << " of N." << (int)N << " size." << (int32_t)gatewaypubkeys.size() << " -> " << depositaddr << std::endl); + } else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(gatewaypubkeys[0])) << OP_CHECKSIG); } else { - fprintf(stderr,"need to generate non-KMD addresses prefix.%d\n",prefix); + if ( N > 1 ) strcpy(depositaddr,CCustomBitcoinAddress(CScriptID(GetScriptForMultisig(M,gatewaypubkeys)),taddr,prefix,prefix2).ToString().c_str()); + else GetCustomscriptaddress(depositaddr,CScript() << ParseHex(HexStr(gatewaypubkeys[0])) << OP_CHECKSIG,taddr,prefix,prefix2); } return(f); - } else fprintf(stderr,"error decoding bind opret\n"); + } else LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "error decoding bind opret" << std::endl); return(0); } -CScript EncodeGatewaysDepositOpRet(uint8_t funcid,std::string coin,uint256 bindtxid,std::vector publishers,std::vectortxids,int32_t height,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) +CScript EncodeGatewaysDepositOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,std::vector publishers,std::vectortxids,int32_t height,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) { CScript opret; uint8_t evalcode = EVAL_GATEWAYS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << bindtxid << publishers << txids << height << cointxid << claimvout << deposithex << proof << destpub << amount); + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << refcoin << bindtxid << publishers << txids << height << cointxid << claimvout << deposithex << proof << destpub << amount); return(opret); } -uint8_t DecodeGatewaysDepositOpRet(const CScript &scriptPubKey,std::string &coin,uint256 &bindtxid,std::vector&publishers,std::vector&txids,int32_t &height,uint256 &cointxid, int32_t &claimvout,std::string &deposithex,std::vector &proof,CPubKey &destpub,int64_t &amount) +uint8_t DecodeGatewaysDepositOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,std::vector&publishers,std::vector&txids,int32_t &height,uint256 &cointxid, int32_t &claimvout,std::string &deposithex,std::vector &proof,CPubKey &destpub,int64_t &amount) { std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> cointxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> cointxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) { return(f); } return(0); } -// encodes payload for the token opret (needs to be added to the tail of it) -CScript EncodeGatewaysClaimOpRet(uint8_t funcid, std::string refcoin, uint256 bindtxid, uint256 deposittxid, CPubKey destpub, int64_t amount) +CScript EncodeGatewaysClaimOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount) { - CScript opret; + CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk; + std::vector pubkeys; + vscript_t vopret; - opret << OP_RETURN << E_MARSHAL(ss << funcid << refcoin << bindtxid << deposittxid << destpub << amount); - return(opret); + pubkeys.push_back(destpub); + vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << deposittxid << destpub << amount); + return(EncodeTokenOpRet(tokenid,pubkeys, make_pair(OPRETID_GATEWAYSDATA, vopret))); } -uint8_t DecodeGatewaysClaimOpRet(const CScript &scriptPubKey,uint256 &tokenid,std::string &refcoin,uint256 &bindtxid,uint256 &deposittxid,CPubKey &destpub,int64_t &amount) +uint8_t DecodeGatewaysClaimOpRet(const CScript &scriptPubKey,uint256 &tokenid,uint256 &bindtxid,std::string &refcoin,uint256 &deposittxid,CPubKey &destpub,int64_t &amount) { - std::vector vopret; uint8_t *script,e,f; - GetOpReturnData(scriptPubKey, vopret); + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> tokenid; ss >> refcoin; ss >> bindtxid; ss >> deposittxid; ss >> destpub; ss >> amount) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> deposittxid; ss >> destpub; ss >> amount) != 0 ) { return(f); } return(0); } -CScript EncodeGatewaysWithdrawOpRet(uint8_t funcid, std::string refcoin, CPubKey withdrawpub, int64_t amount) +CScript EncodeGatewaysWithdrawOpRet(uint8_t funcid,uint256 tokenid,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) { - CScript opret; - opret << OP_RETURN << E_MARSHAL(ss << funcid << refcoin << withdrawpub << amount); - return(opret); + CScript opret; uint8_t evalcode = EVAL_GATEWAYS; struct CCcontract_info *cp,C; CPubKey gatewayspk; + std::vector pubkeys; + vscript_t vopret; + + cp = CCinit(&C,EVAL_GATEWAYS); + gatewayspk = GetUnspendable(cp,0); + pubkeys.push_back(gatewayspk); + vopret = /*opret << OP_RETURN << */ E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << withdrawpub << amount); + return(EncodeTokenOpRet(tokenid,pubkeys, std::make_pair(OPRETID_GATEWAYSDATA, vopret))); } -uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey, uint256 &tokenid, std::string &refcoin, CPubKey &withdrawpub, int64_t &amount) +uint8_t DecodeGatewaysWithdrawOpRet(const CScript &scriptPubKey, uint256& tokenid, uint256 &bindtxid, std::string &refcoin, CPubKey &withdrawpub, int64_t &amount) { - std::vector vopret; uint8_t *script,e,f; + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; - - GetOpReturnData(scriptPubKey, vopret); + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) + { + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> tokenid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) { return(f); } return(0); } -CScript EncodeGatewaysPartialOpRet(uint8_t funcid,int32_t K, CPubKey signerpk, std::string refcoin,std::string hex) +CScript EncodeGatewaysPartialOpRet(uint8_t funcid, uint256 withdrawtxid,std::string refcoin,uint8_t K, CPubKey signerpk,std::string hex) { CScript opret; uint8_t evalcode = EVAL_GATEWAYS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << K << signerpk << refcoin << hex); + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << signerpk << hex); return(opret); } -uint8_t DecodeGatewaysPartialOpRet(const CScript &scriptPubKey,int32_t &K, CPubKey &signerpk, std::string &refcoin,std::string &hex) +uint8_t DecodeGatewaysPartialOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,CPubKey &signerpk,std::string &hex) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> K; ss >> signerpk; ss >> refcoin; ss >> hex) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> signerpk; ss >> hex) != 0 ) { return(f); } return(0); } -CScript EncodeGatewaysCompleteSigningOpRet(uint8_t funcid,std::string refcoin,uint256 withdrawtxid,std::string hex) +CScript EncodeGatewaysCompleteSigningOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint8_t K,std::string hex) { CScript opret; uint8_t evalcode = EVAL_GATEWAYS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << refcoin << withdrawtxid << hex); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << hex); return(opret); } -uint8_t DecodeGatewaysCompleteSigningOpRet(const CScript &scriptPubKey, std::string &refcoin, uint256 &withdrawtxid,std::string &hex) +uint8_t DecodeGatewaysCompleteSigningOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,std::string &hex) { std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> withdrawtxid; ss >> hex) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> hex) != 0 ) { return(f); } return(0); } -CScript EncodeGatewaysMarkDoneOpRet(uint8_t funcid,std::string refcoin,uint256 withdrawtxid) +CScript EncodeGatewaysMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint256 completetxid) { CScript opret; uint8_t evalcode = EVAL_GATEWAYS; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << refcoin << withdrawtxid); + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << completetxid); return(opret); } -uint8_t DecodeGatewaysMarkDoneOpRet(const CScript &scriptPubKey, std::string &refcoin, uint256 &withdrawtxid) +uint8_t DecodeGatewaysMarkDoneOpRet(const CScript &scriptPubKey, uint256 &withdrawtxid, std::string &refcoin, uint256 &completetxid) { std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> withdrawtxid;) != 0 ) + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> completetxid;) != 0 ) { return(f); } @@ -309,12 +343,19 @@ uint8_t DecodeGatewaysMarkDoneOpRet(const CScript &scriptPubKey, std::string &re uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey) { - std::vector vopret; uint8_t *script,e,f; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && (script[0] == EVAL_GATEWAYS || script[0] == EVAL_TOKENS) && E_UNMARSHAL(vopret,ss >> e; ss >> f) != 0 ) + std::vector> oprets; + std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; std::vector pubkeys; uint256 tokenid; + + if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys, oprets)!=0 && GetOpretBlob(oprets, OPRETID_GATEWAYSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) { - if (f == 'B' && f == 'D' && f == 't' && f == 'W' && f == 'P' && f == 'M') + vopret=vOpretExtra; + } + else GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_GATEWAYS) + { + f=script[1]; + if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'P' || f == 'S' || f == 'M') return(f); } return(0); @@ -323,6 +364,7 @@ uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey) int64_t IsGatewaysvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { char destaddr[64]; + if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) { if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) @@ -335,19 +377,20 @@ bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti { static uint256 zerohash; CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; + numvins = tx.vin.size(); numvouts = tx.vout.size(); for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) { - //fprintf(stderr,"vini.%d check mempool\n",i); + LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "vini." << i << " check mempool" << std::endl); if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) return eval->Invalid("cant find vinTx"); else { - //fprintf(stderr,"vini.%d check hash and vout\n",i); + LOGSTREAM("gatewayscc",CCLOG_DEBUG2, stream << "vini." << i << " check hash and vout" << std::endl); if ( hashBlock == zerohash ) return eval->Invalid("cant Gateways from mempool"); if ( (assetoshis= IsGatewaysvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) @@ -357,132 +400,53 @@ bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti } for (i=0; iInvalid("mismatched inputs != outputs + txfee"); } else return(true); } -static int32_t myIs_coinaddr_inmempoolvout(char *coinaddr) +int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 cointxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2) { - int32_t i,n; char destaddr[64]; - BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) - { - const CTransaction &tx = e.GetTx(); - if ( (n= tx.vout.size()) > 0 ) - { - const uint256 &txid = tx.GetHash(); - for (i=0; idata; - txid = zeroid; - char str[65]; - //fprintf(stderr,"start reverse scan %s\n",uint256_str(str,batontxid)); - while ( myGetTransaction(batontxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) - { - //fprintf(stderr,"check %s\n",uint256_str(str,batontxid)); - if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,bhash,pk,data) == 'D' && oracletxid == reforacletxid ) - { - //fprintf(stderr,"decoded %s\n",uint256_str(str,batontxid)); - if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height ) - { - len = oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()); - len2 = oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()); - char str2[65]; fprintf(stderr,"found merkleht.%d len.%d len2.%d %s %s\n",(int32_t)merkleht,len,len2,uint256_str(str,hash),uint256_str(str2,mhash)); - if ( len == sizeof(hash)+sizeof(int32_t) && len2 == 2*sizeof(mhash)+sizeof(int32_t) && mhash != zeroid ) - { - txid = batontxid; - //fprintf(stderr,"set txid\n"); - return(mhash); - } - else - { - //fprintf(stderr,"missing hash\n"); - return(zeroid); - } - } //else fprintf(stderr,"height.%d vs search ht.%d\n",(int32_t)merkleht,(int32_t)height); - batontxid = bhash; - //fprintf(stderr,"new hash %s\n",uint256_str(str,batontxid)); - } else break; - } - fprintf(stderr,"end of loop\n"); - return(zeroid); -} - -int32_t GatewaysCointxidExists(struct CCcontract_info *cp,uint256 cointxid) // dont forget to check mempool! -{ - char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock; - std::vector > addressIndex; - CCtxidaddr(txidaddr,cointxid); - SetCCtxids(addressIndex,txidaddr); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - return(-1); - } - return(myIs_coinaddr_inmempoolvout(txidaddr)); -} - -/* Get the block merkle root for a proof - * IN: proofData - * OUT: merkle root - * OUT: transaction IDS - */ -uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids) -{ - CMerkleBlock merkleBlock; - if (!E_UNMARSHAL(proofData, ss >> merkleBlock)) - return uint256(); - return merkleBlock.txn.ExtractMatches(txids); -} - -int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 cointxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub) -{ - std::vector txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; char destaddr[64],destpubaddr[64],claimaddr[64],str[65],str2[65]; int32_t i,numvouts; int64_t nValue = 0; + std::vector txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; + char destaddr[64],destpubaddr[64],claimaddr[64]; int32_t i,numvouts; int64_t nValue = 0; + if ( myGetTransaction(oracletxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - fprintf(stderr,"GatewaysVerify cant find oracletxid %s\n",uint256_str(str,oracletxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify cant find oracletxid " << oracletxid.GetHex() << std::endl); return(0); } if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin ) { - fprintf(stderr,"GatewaysVerify mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str()); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify mismatched oracle name " << name << " != " << refcoin << std::endl); return(0); } proofroot = BitcoinGetProofMerkleRoot(proof,txids); if ( proofroot != merkleroot ) { - fprintf(stderr,"GatewaysVerify mismatched merkleroot %s != %s\n",uint256_str(str,proofroot),uint256_str(str2,merkleroot)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl); return(0); } + if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) + { + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "GatewaysVerify invalid proof for this cointxid" << std::endl); + return 0; + } if ( DecodeHexTx(tx,deposithex) != 0 ) { - Getscriptaddress(claimaddr,tx.vout[claimvout].scriptPubKey); - Getscriptaddress(destpubaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); + GetCustomscriptaddress(claimaddr,tx.vout[claimvout].scriptPubKey,taddr,prefix,prefix2); + GetCustomscriptaddress(destpubaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG,taddr,prefix,prefix2); if ( strcmp(claimaddr,destpubaddr) == 0 ) { for (i=0; i publishers; std::vectortxids; uint256 bindtxid,cointxid; std::vector proof; CPubKey claimpubkey; if ( (numvouts= tx.vout.size()) > 0 ) { - if ( DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,coin,bindtxid,publishers,txids,height,cointxid,claimvout,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk ) + if ( DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk ) { - // coin, bindtxid, publishers - fprintf(stderr,"need to validate deposittxid more\n"); return(amount); } } return(0); } +int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 reftokenid) +{ + char markeraddr[64],depositaddr[64]; std::string coin; int32_t numvouts; int64_t totalsupply; uint256 tokenid,oracletxid,hashBlock; + uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; CTransaction tx; + std::vector > addressIndex; + + _GetCCaddress(markeraddr,EVAL_GATEWAYS,gatewayspk); + SetCCtxids(addressIndex,markeraddr,true); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + if ( myGetTransaction(it->first.txhash,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)=='B' ) + { + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' ) + { + if ( tokenid == reftokenid ) + { + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "trying to bind an existing tokenid" << std::endl); + return(1); + } + } + } + } + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + + if ((numvouts=txmempool.vout.size()) > 0 && DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)=='B') + if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' && + tokenid == reftokenid) + return(1); + } + + return(0); +} + bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],M,N,taddr,prefix,prefix2; - char str[65],destaddr[64],depositaddr[65],validationError[512]; - std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t totalsupply,amount,tmpamount; - uint256 hashblock,txid,bindtxid,deposittxid,tokenidClaim,oracletxid,tokenidBind,cointxid,tmptxid,tmpxtxid2,merkleroot,mhash; CTransaction bindtx,deposittx,oracletx; - std::string refcoin,tmprefcoin,deposithex; CPubKey destpub,tmpdestpub; + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],K,M,N,taddr,prefix,prefix2,wiftype; + char str[65],destaddr[65],depositaddr[65],gatewaystokensaddr[65],validationError[512]; + std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t fullsupply,totalsupply,amount,tmpamount; + uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,completetxid,tokenid,tmptokenid,oracletxid,bindtokenid,cointxid,tmptxid,merkleroot,mhash; CTransaction bindtx,tmptx; + std::string refcoin,tmprefcoin,hex,name,description,format; CPubKey pubkey,tmppubkey,gatewayspk; - fprintf(stderr,"return true without gateways validation\n"); - return(true); numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -532,94 +528,131 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & return eval->Invalid("no vouts"); else { - // for (i=0; iInvalid("illegal normal vini"); - // } - // } - //fprintf(stderr,"check amounts\n"); + //LogPrint("gatewayscc-1","check amounts\n"); // if ( GatewaysExactAmounts(cp,eval,tx,1,10000) == false ) // { - // fprintf(stderr,"Gatewaysget invalid amount\n"); - // return false; + // return eval->Invalid("invalid inputs vs. outputs!"); // } // else // { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - + gatewayspk = GetUnspendable(cp,0); + GetTokensCCaddress(cp, gatewaystokensaddr, gatewayspk); if ( (funcid = DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey)) != 0) { switch ( funcid ) { case 'B': //vin.0: normal input - //vout.0: CC vout txfee marker - //vout.n-1: opreturn - 'B' coin tokenid totalsupply oracletxid M N pubkeys taddr prefix prefix2 + //vin.1: CC input of tokens + //vout.0: CC vout of gateways tokens to gateways tokens CC address + //vout.1: CC vout marker + //vout.n-1: opreturn - 'B' tokenid coin totalsupply oracletxid M N pubkeys taddr prefix prefix2 wiftype return eval->Invalid("unexpected GatewaysValidate for gatewaysbind!"); break; case 'D': //vin.0: normal input - //vout.0: CC vout txfee marker to destination pubkey - //vout.1: normal output txfee marker to txidaddr - //vout.n-1: opreturn - 'D' coin bindtxid publishers txids height cointxid deposithex proof destpub amount + //vout.0: CC vout marker to destination pubkey + //vout.1: normal output marker to txidaddr + //vout.n-1: opreturn - 'D' bindtxid coin publishers txids height cointxid claimvout deposithex proof destpub amount return eval->Invalid("unexpected GatewaysValidate for gatewaysdeposit!"); break; - case 't': + case 'C': //vin.0: normal input - //vin.1: CC input of converted token to gateways eval code + //vin.1: CC input of gateways tokens //vin.2: CC input of marker from gatewaysdeposit tx - //vout.0: CC vout of total tokens from deposit amount to asset eval code - //(vout.1): CC vout if there is change of unused tokens back to owner of tokens (deposit amount less than available tokens) - //vout.n-1: opreturn - 't' tokenid zeroid 0 mypubkey (NOTE: opreturn is with asset eval code) - if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysClaimOpRet(tx.vout[numvouts-1].scriptPubKey,tokenidClaim,refcoin,bindtxid,deposittxid,destpub,amount)==0) + //vout.0: CC vout of tokens from deposit amount to destinatoin pubkey + //vout.1: CC vout change of gateways tokens to gateways tokens CC address (if any) + //vout.n-1: opreturn - 'C' tokenid bindtxid coin deposittxid destpub amount + if ((numvouts=tx.vout.size()) < 1 || DecodeGatewaysClaimOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,refcoin,deposittxid,pubkey,amount)!='C') return eval->Invalid("invalid gatewaysclaim OP_RETURN data!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for gatewaysClaim!"); - else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for gatewaysClaim!"); - else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for gatewaysClaim!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.0 is CC for gatewaysClaim!"); - else if ( numvouts > 2 && tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.1 is CC for gatewaysClaim!"); - else if (myGetTransaction(bindtxid,bindtx,hashblock) == 0) + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) return eval->Invalid("invalid gatewaysbind txid!"); - else if ((numvouts=bindtx.vout.size()) > 0 && DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,tmprefcoin,tokenidBind,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 'B') + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysbind!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysbind!"); + else if ( ConstrainVout(tmptx.vout[0],1,gatewaystokensaddr,totalsupply)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysbind!"); + else if ( ConstrainVout(tmptx.vout[1],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysbind!"); else if (tmprefcoin!=refcoin) - return eval->Invalid("refcoin different in bind tx"); - else if (tokenidClaim!=tokenidBind) + return eval->Invalid("refcoin different than in bind tx"); + else if (tmptokenid!=tokenid) return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); + else if ( N == 0 || N > 15 || M > N ) + return eval->Invalid("invalid MofN in gatewaysbind"); + else if (pubkeys.size()!=N) + { + sprintf(validationError,"not enough pubkeys(%ld) for N.%d gatewaysbind ",pubkeys.size(),N); + return eval->Invalid(validationError); + } + else if ( (fullsupply=CCfullsupply(tokenid)) != totalsupply ) + { + sprintf(validationError,"Gateway bind.%s (%s) globaladdr.%s totalsupply %.8f != fullsupply %.8f\n",refcoin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)totalsupply/COIN,(double)fullsupply/COIN); + return eval->Invalid(validationError); + } + else if (myGetTransaction(oracletxid,tmptx,hashblock) == 0 || (numvouts=tmptx.vout.size()) <= 0 ) + { + sprintf(validationError,"cant find oracletxid %s\n",uint256_str(str,oracletxid)); + return eval->Invalid(validationError); + } + else if ( DecodeOraclesCreateOpRet(tmptx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' ) + { + sprintf(validationError,"mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str()); + return eval->Invalid(validationError); + } + else if (format.size()!=3 || strncmp(format.c_str(),"Ihh",3)!=0) + { + sprintf(validationError,"illegal format %s != Ihh\n",format.c_str()); + return eval->Invalid(validationError); + } else if (komodo_txnotarizedconfirmed(bindtxid) == false) return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); - else if (myGetTransaction(deposittxid,deposittx,hashblock) == 0) + else if (myGetTransaction(deposittxid,tmptx,hashblock) == 0) return eval->Invalid("invalid gatewaysdeposittxid!"); - else if ((numvouts=deposittx.vout.size()) > 0 && DecodeGatewaysDepositOpRet(deposittx.vout[numvouts-1].scriptPubKey,tmprefcoin,tmptxid,tmppublishers,txids,height,cointxid,claimvout,deposithex,proof,tmpdestpub,tmpamount) != 'D') + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysDepositOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptxid,tmprefcoin,tmppublishers,txids,height,cointxid,claimvout,hex,proof,tmppubkey,tmpamount) != 'D') return eval->Invalid("invalid gatewaysdeposit OP_RETURN data!"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysdeposit!"); + else if ( GetCCaddress(cp,destaddr,tmppubkey)==0 || ConstrainVout(tmptx.vout[0],1,destaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid CC marker vout for gatewaysdeposit!"); + else if ( CCtxidaddr(destaddr,cointxid)==CPubKey() || ConstrainVout(tmptx.vout[1],0,destaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid normal marker vout for gatewaysdeposit!"); else if (tmprefcoin!=refcoin) - return eval->Invalid("refcoin different in deposit tx"); + return eval->Invalid("refcoin different than in deposit tx"); else if (bindtxid!=tmptxid) - return eval->Invalid("bindtxid does not match to bindtxid from gatewaysdeposit"); + return eval->Invalid("bindtxid does not match to bindtxid from gatewaysdeposit"); else if (tmpamount>totalsupply) return eval->Invalid("deposit amount greater then bind total supply"); else if (komodo_txnotarizedconfirmed(deposittxid) == false) return eval->Invalid("gatewaysdeposit tx is not yet confirmed(notarised)!"); - else if (amount>tmpamount) - return eval->Invalid("claimed amount greater then deposit amount"); - else if (destpub!=tmpdestpub) - return eval->Invalid("destination pubkey different than in deposit tx"); + else if (myGetTransaction(tx.vin[2].prevout.hash,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysdeposittxid!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewaysclaim!"); + else if (IsCCInput(tx.vin[1].scriptSig) == 0) + return eval->Invalid("vin.1 is CC for gatewaysclaim!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || myGetTransaction(tx.vin[2].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker for gatewaysclaim or invalid marker amount!"); + else if (_GetCCaddress(destaddr,EVAL_TOKENS,pubkey)==0 || ConstrainVout(tx.vout[0],1,destaddr,amount)==0) + return eval->Invalid("invalid vout tokens to destpub for gatewaysclaim!"); + else if (numvouts>2 && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || ConstrainVout(tx.vout[1],1,gatewaystokensaddr,tmptx.vout[tx.vin[1].prevout.n].nValue-amount)==0)) + return eval->Invalid("invalid CC change vout for gatewaysclaim!"); + else if (amount!=tmpamount) + return eval->Invalid("claimed amount different then deposit amount"); + else if (tx.vout[0].nValue!=amount) + return eval->Invalid("claim amount not matching amount in opret"); + else if (pubkey!=tmppubkey) + return eval->Invalid("claim destination pubkey different than in deposit tx"); else { int32_t m; merkleroot = zeroid; for (i=m=0; iInvalid(validationError); } - if ( GatewaysCointxidExists(cp,cointxid) != 0 ) - { - sprintf(validationError,"cointxid.%s already exists\n",uint256_str(str,cointxid)); - return eval->Invalid(validationError); - } - if (GatewaysVerify(depositaddr,oracletxid,claimvout,tmprefcoin,cointxid,deposithex,proof,merkleroot,destpub)!=amount) - return eval->Invalid("deposittxid didnt validate\n"); + else if (GatewaysVerify(depositaddr,oracletxid,claimvout,tmprefcoin,cointxid,hex,proof,merkleroot,pubkey,taddr,prefix,prefix2)!=amount) + return eval->Invalid("external deposit not verified\n"); } break; case 'W': //vin.0: normal input - //vin.1: CC input of converted token back to gateways eval code - //vout.0: CC vout of tokens back to gateways CC address - //vout.1: normal vout txfee marker to withdraw destination pubkey - //vout.2: CC vout txfee marker to gateways CC address - //vout.n-2: CC vout if there is change of unused tokens back to owner of tokens (withdraw amount less than owner available tokens) - //vout.n-1: opreturn - 'W' tokenid refcoin withdrawpub amount + //vin.1: CC input of tokens + //vout.0: CC vout marker to gateways CC address + //vout.1: CC vout of gateways tokens back to gateways tokens CC address + //vout.2: CC vout change of tokens back to owners pubkey (if any) + //vout.n-1: opreturn - 'W' tokenid bindtxid refcoin withdrawpub amount + return eval->Invalid("unexpected GatewaysValidate for gatewaysWithdraw!"); break; case 'P': //vin.0: normal input - //(vin.1): CC input form previous marker of gatewayspartialsign tx (if exists) - //vout.0: CC vout 5k sat marker to senders pubKey - //vout.n-1: opreturn - 'P' number_of_signs mypk refcoin hex + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'P' withdrawtxid refcoin number_of_signs mypk hex + if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,pubkey,hex)!='P') + return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (tmptokenid!=tokenid) + return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayspartialsign!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayspartialsign or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!"); + else if (K>M) + return eval->Invalid("invalid number of signs!"); break; - case 'C': - //vin.0: CC input from gatewayswithdraw tx marker to gateways CC address - //vout.0: CC vout txfee marker to gateways CC address - //vout.n-1: opreturn - 'C' refcoin cointxid external_tx_hex + case 'S': + //vin.0: normal input + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'S' withdrawtxid refcoin hex + if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (tmptokenid!=tokenid) + return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayscompletesigning!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayscompletesigning or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!"); + else if (KInvalid("invalid number of signs!"); break; case 'M': - //vin.0: CC input from gatewayscompletesigning tx marker to gateways CC address - //vout.0: opreturn - 'M' refcoin cointxid + //vin.0: normal input + //vin.1: CC input of gatewayscompletesigning tx marker to gateways CC address + //vout.0: opreturn - 'M' withdrawtxid refcoin completetxid + if ((numvouts=tx.vout.size()) > 0 && DecodeGatewaysMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,completetxid)!='M') + return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!"); + else if (myGetTransaction(completetxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewayscompletesigning txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysCompleteSigningOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmprefcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (komodo_txnotarizedconfirmed(completetxid) == false) + return eval->Invalid("gatewayscompletesigning tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (tmptokenid!=tokenid) + return eval->Invalid("tokenid does not match tokenid from gatewaysbind"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysmarkdone!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewaysmarkdone or invalid marker amount!"); + else if (KInvalid("invalid number of signs!"); break; } } - - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) - fprintf(stderr,"Gatewaysget validated\n"); - else fprintf(stderr,"Gatewaysget invalid\n"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "Gateways tx validated" << std::endl); + else fprintf(stderr,"Gateways tx invalid\n"); return(retval); // } } @@ -683,192 +819,197 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction & // helper functions for rpc calls in rpcwallet.cpp -int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 reftokenid,int64_t total,int32_t maxinputs) +int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 bindtxid,int64_t total,int32_t maxinputs) { - char coinaddr[64],destaddr[64]; int64_t threshold,nValue,price,totalinputs = 0; uint256 tokenid,txid,hashBlock; std::vector origpubkey; std::vector vopret; CTransaction vintx; int32_t j,vout,n = 0; uint8_t evalcode,funcid; - std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); - //fprintf(stderr,"check %s for gateway inputs\n",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; - for (j=0; jsecond.satoshis/COIN); - if ( strcmp(destaddr,coinaddr) != 0 && strcmp(destaddr,cp->unspendableCCaddr) != 0 && strcmp(destaddr,cp->unspendableaddr2) != 0 ) - continue; - GetOpReturnData(vintx.vout[vintx.vout.size()-1].scriptPubKey, vopret); - if ( E_UNMARSHAL(vopret,ss >> evalcode; ss >> funcid; ss >> tokenid) != 0 ) - { - tokenid = revuint256(tokenid); - char str[65],str2[65]; fprintf(stderr,"vout.%d %d:%d (%c) check for reftokenid.%s vs %s %.8f\n",vout,evalcode,cp->evalcode,funcid,uint256_str(str,reftokenid),uint256_str(str2,tokenid),(double)vintx.vout[vout].nValue/COIN); - if ( tokenid == reftokenid && funcid == 't' && (nValue= vintx.vout[vout].nValue) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) - { - //fprintf(stderr,"total %llu maxinputs.%d %.8f\n",(long long)total,maxinputs,(double)it->second.satoshis/COIN); - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - //nValue = it->second.satoshis; - totalinputs += nValue; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } - } - return(totalinputs); -} + char coinaddr[64],depositaddr[64]; int64_t threshold,nValue,price,totalinputs = 0,totalsupply,amount; + CTransaction vintx,bindtx; int32_t vout,numvouts,n = 0; uint8_t M,N,evalcode,funcid,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + std::vector > unspentOutputs; std::string refcoin,tmprefcoin; CPubKey withdrawpub,destpub; + uint256 tokenid,txid,oracletxid,tmpbindtxid,tmptokenid,deposittxid,hashBlock; -int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 reftokenid) // dont forget to check mempool! -{ - char markeraddr[64],depositaddr[64]; std::string coin; int32_t numvouts; int64_t totalsupply; uint256 tokenid,oracletxid,hashBlock; uint8_t M,N,taddr,prefix,prefix2; std::vector pubkeys; CTransaction tx; - std::vector > addressIndex; - _GetCCaddress(markeraddr,EVAL_GATEWAYS,gatewayspk); - fprintf(stderr,"bind markeraddr.(%s) need to scan mempool also\n",markeraddr); - SetCCtxids(addressIndex,markeraddr); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + if ( GetTransaction(bindtxid,bindtx,hashBlock,false) != 0 ) { - if ( GetTransaction(it->first.txhash,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) + if ((numvouts=bindtx.vout.size())!=0 && DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,tokenid,refcoin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B') { - if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) == 'B' ) + GetTokensCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr,true); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "check " << coinaddr << " for gateway inputs" << std::endl); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ( tokenid == reftokenid ) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - fprintf(stderr,"trying to bind an existing tokenid\n"); - return(1); + funcid=DecodeGatewaysOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey); + if ((vout==0 && funcid=='B' && bindtxid==txid && total != 0 && maxinputs != 0) || + (vout==1 && funcid=='W' && DecodeGatewaysWithdrawOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,withdrawpub,amount) == 'W' && + tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0) || + (vout==1 && funcid=='C' && DecodeGatewaysClaimOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,tmptokenid,tmpbindtxid,tmprefcoin,deposittxid,destpub,amount) == 'C' && + tmpbindtxid==bindtxid && tmprefcoin==refcoin && tmptokenid==tokenid && total != 0 && maxinputs != 0)) + { + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + totalinputs += it->second.satoshis; + n++; + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; + } } } + return(totalinputs); } + else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "invalid GatewaysBind" << std::endl); } + else LOGSTREAM("gatewayscc",CCLOG_INFO, stream << "can't find GatewaysBind txid" << std::endl); return(0); } -std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys) +std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx; uint8_t taddr,prefix,prefix2; CPubKey mypk,gatewayspk; CScript opret; uint256 hashBlock; struct CCcontract_info *cp,C; std::string name,description,format; int32_t i,numvouts; int64_t fullsupply; char destaddr[64],coinaddr[64],str[65],*fstr; + CTransaction oracletx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,gatewayspk; CScript opret; uint256 hashBlock; + struct CCcontract_info *cp,*cpTokens,C,CTokens; std::string name,description,format; int32_t i,numvouts; int64_t fullsupply; + char destaddr[64],coinaddr[64],myTokenCCaddr[64],str[65],*fstr; + cp = CCinit(&C,EVAL_GATEWAYS); - if ( strcmp((char *)"KMD",coin.c_str()) == 0 ) + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if (coin=="KMD") { - taddr = 0; - prefix = 60; - prefix2 = 85; + prefix = KMD_PUBTYPE; + prefix2 = KMD_P2SHTYPE; + wiftype = KMD_WIFTYPE; + taddr = KMD_TADDR; } else { - fprintf(stderr,"set taddr, prefix, prefix2 for %s\n",coin.c_str()); - taddr = 0; - prefix = 60; - prefix2 = 85; + prefix = p1; + prefix2 = p2; + wiftype = p3; + taddr = p4; + LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "set prefix " << prefix << ", prefix2 " << prefix2 << ", wiftype " << wiftype << ", taddr " << taddr << " for " << coin << std::endl); } if ( N == 0 || N > 15 || M > N ) { - fprintf(stderr,"illegal M.%d or N.%d\n",M,N); + CCerror = strprintf("illegal M.%d or N.%d",M,N); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if ( pubkeys.size() != N ) { - fprintf(stderr,"M.%d N.%d but pubkeys[%d]\n",M,N,(int32_t)pubkeys.size()); + CCerror = strprintf("M.%d N.%d but pubkeys[%d]",M,N,(int32_t)pubkeys.size()); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } for (i=0; iunspendableCCaddr,(double)totalsupply/COIN,(double)fullsupply/COIN); + CCerror = strprintf("Gateway bind.%s (%s) globaladdr.%s totalsupply %.8f != fullsupply %.8f",coin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)totalsupply/COIN,(double)fullsupply/COIN); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( CCtoken_balance(destaddr,tokenid) != totalsupply ) + if ( CCtoken_balance(myTokenCCaddr,tokenid) != totalsupply ) { - fprintf(stderr,"Gateway bind.%s (%s) destaddr.%s globaladdr.%s token balance %.8f != %.8f\n",coin.c_str(),uint256_str(str,tokenid),destaddr,cp->unspendableCCaddr,(double)CCtoken_balance(destaddr,tokenid)/COIN,(double)totalsupply/COIN); + CCerror = strprintf("token balance on %s %.8f != %.8f",myTokenCCaddr,(double)CCtoken_balance(myTokenCCaddr,tokenid)/COIN,(double)totalsupply/COIN); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if ( GetTransaction(oracletxid,oracletx,hashBlock,false) == 0 || (numvouts= oracletx.vout.size()) <= 0 ) { - fprintf(stderr,"cant find oracletxid %s\n",uint256_str(str,oracletxid)); + CCerror = strprintf("cant find oracletxid %s",uint256_str(str,oracletxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' ) { - fprintf(stderr,"mismatched oracle name %s != %s\n",name.c_str(),coin.c_str()); + CCerror = strprintf("mismatched oracle name %s != %s",name.c_str(),coin.c_str()); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( (fstr= (char *)format.c_str()) == 0 || strncmp(fstr,"Ihh",3) != 0 ) + if ( (fstr=(char *)format.c_str()) == 0 || strncmp(fstr,"Ihh",3) != 0 ) { - fprintf(stderr,"illegal format (%s) != (%s)\n",fstr,(char *)"Ihh"); + CCerror = strprintf("illegal format (%s) != (%s)",fstr,(char *)"Ihh"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( GatewaysBindExists(cp,gatewayspk,tokenid) != 0 ) // dont forget to check mempool! + if ( GatewaysBindExists(cp,gatewayspk,tokenid) != 0 ) { - fprintf(stderr,"Gateway bind.%s (%s) already exists\n",coin.c_str(),uint256_str(str,tokenid)); + CCerror = strprintf("Gateway bind.%s (%s) already exists",coin.c_str(),uint256_str(str,tokenid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gatewayspk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2))); + if (AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, totalsupply, 64)>0) + { + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,totalsupply,gatewayspk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,gatewayspk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype))); + } } CCerror = strprintf("cant find enough inputs"); - fprintf(stderr,"%s\n", CCerror.c_str() ); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction bindtx; CPubKey mypk,gatewayspk; uint256 oracletxid,merkleroot,mhash,hashBlock,tokenid,txid; - int64_t totalsupply; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2; std::string coin; struct CCcontract_info *cp,C; + CTransaction bindtx; CPubKey mypk; uint256 oracletxid,merkleroot,mhash,hashBlock,tokenid,txid; + int64_t totalsupply; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C; std::vector pubkeys,publishers; std::vectortxids; char str[67],depositaddr[64],txidaddr[64]; cp = CCinit(&C,EVAL_GATEWAYS); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); - gatewayspk = GetUnspendable(cp,0); - //fprintf(stderr,"GatewaysDeposit ht.%d %s %.8f numpks.%d\n",height,refcoin.c_str(),(double)amount/COIN,(int32_t)pubkeys.size()); + LOGSTREAM("gatewayscc",CCLOG_DEBUG1, stream << "GatewaysDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl); if ( GetTransaction(bindtxid,bindtx,hashBlock,false) == 0 || (numvouts= bindtx.vout.size()) <= 0 ) { - fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 'B' || refcoin != coin ) + if ( DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) { - fprintf(stderr,"invalid coin - bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); + CCerror = strprintf("invalid coin - bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(bindtxid)==false) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } n = (int32_t)pubkeys.size(); merkleroot = zeroid; for (i=m=0; i 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,4) > 0 ) { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,destpub)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysDepositOpRet('D',coin,bindtxid,publishers,txids,height,cointxid,claimvout,deposithex,proof,destpub,amount))); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,destpub)); + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysDepositOpRet('D',bindtxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,destpub,amount))); } - fprintf(stderr,"cant find enough inputs\n"); + CCerror = strprintf("cant find enough inputs"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction tx; CPubKey mypk,gatewayspk,tmpdestpub; struct CCcontract_info *cp,C; uint8_t M,N,taddr,prefix,prefix2; + CTransaction tx; CPubKey mypk,gatewayspk,tmpdestpub; struct CCcontract_info *cp,C; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin, deposithex; std::vector msigpubkeys,publishers; int64_t totalsupply,depositamount,tmpamount,inputs,CCchange=0; int32_t numvouts,claimvout,height; std::vector proof; uint256 hashBlock,tokenid,oracletxid,tmptxid,cointxid; char str[65],depositaddr[64],coinaddr[64],destaddr[64]; std::vector txids; @@ -922,202 +1064,477 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui gatewayspk = GetUnspendable(cp,0); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin ) + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || coin != refcoin ) { - fprintf(stderr,"invalid coin - bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); + CCerror = strprintf("invalid coin - bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(bindtxid)==false) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if ( GetTransaction(deposittxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - fprintf(stderr,"cant find deposittxid %s\n",uint256_str(str,bindtxid)); + CCerror = strprintf("cant find deposittxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if (DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,coin,tmptxid,publishers,txids,height,cointxid,claimvout,deposithex,proof,tmpdestpub,tmpamount) != 'D' || coin != refcoin) + if (DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmptxid,coin,publishers,txids,height,cointxid,claimvout,deposithex,proof,tmpdestpub,tmpamount) != 'D' || coin != refcoin) { - fprintf(stderr,"invalid coin - deposittxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); + CCerror = strprintf("invalid coin - deposittxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(deposittxid)==false) + { + CCerror = strprintf("gatewaysdeposit tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if (tmpdestpub!=destpub) { - fprintf(stderr,"different destination pubkey from desdeposittxid\n"); + CCerror = strprintf("different destination pubkey from desdeposit tx"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( (depositamount= GatewaysDepositval(tx,mypk)) != amount ) + if ( (depositamount=GatewaysDepositval(tx,mypk)) != amount ) { - fprintf(stderr,"invalid Gateways deposittxid %s %.8f != %.8f\n",uint256_str(str,deposittxid),(double)depositamount/COIN,(double)amount/COIN); + CCerror = strprintf("invalid Gateways deposittxid %s %.8f != %.8f",uint256_str(str,deposittxid),(double)depositamount/COIN,(double)amount/COIN); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - //fprintf(stderr,"depositaddr.(%s) vs %s\n",depositaddr,cp->unspendableaddr2); if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) { - ///////// if ( (inputs= AddGatewaysInputs(cp,mtx,gatewayspk,tokenid,amount,60)) > 0 ) - if ((inputs = AddTokenCCInputs(cp, mtx, gatewayspk, tokenid, amount, 60)) > 0) + if ((inputs=AddGatewaysInputs(cp, mtx, gatewayspk, bindtxid, amount, 60)) > 0) { - if ( inputs > amount ) - CCchange = (inputs - amount); - _GetCCaddress(destaddr,EVAL_GATEWAYS,mypk); - //printf("expecting deposittxid/v0 to be to %s\n",destaddr); - mtx.vin.push_back(CTxIn(deposittxid,0,CScript())); // triggers EVAL_GATEWAYS validation - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,amount,mypk)); // transfer back to normal token - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CCchange,gatewayspk)); - - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(mypk); // the pubkey where tokens are going (vout[0]) - - return(FinalizeCCTx(0,cp,mtx,mypk,txfee, - EncodeTokenOpRet('t', EVAL_GATEWAYS, tokenid, voutTokenPubkeys, - EncodeGatewaysClaimOpRet('t', refcoin, bindtxid, deposittxid, destpub, amount)))); // yes, 't' is passed twice + if ( inputs > amount ) CCchange = (inputs - amount); + mtx.vin.push_back(CTxIn(deposittxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,amount,destpub)); + if ( CCchange != 0 ) mtx.vout.push_back(MakeTokensCC1vout(EVAL_GATEWAYS,CCchange,gatewayspk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysClaimOpRet('C',tokenid,bindtxid,refcoin,deposittxid,destpub,amount))); } } CCerror = strprintf("cant find enough inputs or mismatched total"); - fprintf(stderr,"%s\n", CCerror.c_str() ); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction tx; - CPubKey mypk, gatewayspk; - uint256 tokenid,hashBlock,oracletxid; int32_t numvouts; int64_t totalsupply,inputs,CCchange=0; uint8_t M,N,taddr,prefix,prefix2; std::string coin; - std::vector msigpubkeys; char depositaddr[64],str[65],coinaddr[64]; - struct CCcontract_info *cpGateways, gatewaysC; - struct CCcontract_info *cpTokens, tokensC; - - cpGateways = CCinit(&gatewaysC, EVAL_GATEWAYS); - cpTokens = CCinit(&tokensC, EVAL_TOKENS); + CTransaction tx; CPubKey mypk,gatewayspk,signerpk; uint256 txid,tokenid,hashBlock,oracletxid,tmptokenid,tmpbindtxid,withdrawtxid; int32_t vout,numvouts; + int64_t nValue,totalsupply,inputs,CCchange=0,tmpamount; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex; + std::vector msigpubkeys; char depositaddr[64],str[65],coinaddr[64]; struct CCcontract_info *cp,C,*cpTokens,CTokens; + std::vector > unspentOutputs; + cp = CCinit(&C,EVAL_GATEWAYS); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); if ( txfee == 0 ) txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - gatewayspk = GetUnspendable(cpGateways, 0); + gatewayspk = GetUnspendable(cp, 0); if( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin ) + if( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || coin != refcoin ) { - fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str()); + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if( AddNormalinputs(mtx, mypk, 3*txfee, 4) > 0 ) + if (komodo_txnotarizedconfirmed(bindtxid)==false) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + K=0; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P')) + { + if (funcid=='W' && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmptokenid==tokenid && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + else if (funcid=='P' && DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)=='P' && + GetTransaction(withdrawtxid,tx,hashBlock,false)!=0 && (numvouts=tx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmptokenid==tokenid && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + } + if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE, 4) > 0 ) { - /////if ( (inputs= AddGatewaysInputs(cp,mtx,mypk,tokenid,amount,60)) > 0 ) if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, amount, 60)) > 0) { - if ( inputs > amount ) - CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, gatewayspk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS, txfee, gatewayspk)); - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); - - char unspendableGatewaysAddr[64]; - GetCCaddress(cpGateways, unspendableGatewaysAddr, gatewayspk); - - uint8_t unspendableGatewaysPrivkey[32]; - GetUnspendable(cpGateways, unspendableGatewaysPrivkey); - - // add additional unspendable addr from Gateways: - CCaddr2set(cpTokens, EVAL_GATEWAYS, gatewayspk, unspendableGatewaysPrivkey, unspendableGatewaysAddr); - - std::vector voutTokenPubkeys; - voutTokenPubkeys.push_back(gatewayspk); // the pubkey where tokens are going vout[0] (and for checking the 'change' the Tokens contract will find pubkeys itself) - - return(FinalizeCCTx(0, cpTokens, mtx, mypk, txfee, - EncodeTokenOpRet('t', EVAL_GATEWAYS, tokenid, voutTokenPubkeys, - EncodeGatewaysWithdrawOpRet('W', refcoin, withdrawpub, amount)))); + if ( inputs > amount ) CCchange = (inputs - amount); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_GATEWAYS,amount,gatewayspk)); + if ( CCchange != 0 ) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); + return(FinalizeCCTx(0, cpTokens, mtx, mypk, txfee,EncodeGatewaysWithdrawOpRet('W',tokenid,bindtxid,refcoin,withdrawpub,amount))); + } + else + { + CCerror = strprintf("not enough balance of tokens for withdraw"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } - CCerror = strprintf("cant find enough token inputs or mismatched total"); - fprintf(stderr, "%s\n", CCerror.c_str()); - return(""); - } - CCerror = strprintf("cant find enough normal inputs or mismatched total"); - fprintf(stderr,"%s\n", CCerror.c_str() ); + CCerror = strprintf("cant find enough normal inputs"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } -std::string GatewaysPartialSign(uint64_t txfee,uint256 txid,std::string refcoin, std::string hex) +std::string GatewaysPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,txidaddrpk,signerpk; struct CCcontract_info *cp,C; CTransaction tx; - std::vector > unspentOutputs; char txidaddr[65]; - int32_t maxK,K=0; uint256 tmptxid,parttxid,hashBlock; + CPubKey mypk,withdrawpub,signerpk,gatewayspk; struct CCcontract_info *cp,C; CTransaction tx,tmptx; + std::vector > unspentOutputs; char funcid,str[65],depositaddr[64]; + int32_t numvouts; uint256 withdrawtxid,hashBlock,bindtxid,tokenid,oracletxid,tmptokenid; std::string coin,tmphex; int64_t amount,totalsupply; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + cp = CCinit(&C,EVAL_GATEWAYS); if ( txfee == 0 ) - txfee = 5000; + txfee = 10000; mypk = pubkey2pk(Mypubkey()); - txidaddrpk=CCtxidaddr(txidaddr,txid); - SetCCunspents(unspentOutputs,txidaddr); - maxK=0; - if (unspentOutputs.size()==0) + gatewayspk = GetUnspendable(cp,0); + if (GetTransaction(lasttxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) { - if (AddNormalinputs(mtx,mypk,2*txfee,3)==0) fprintf(stderr,"error adding funds for partialsign\n"); + CCerror = strprintf("can't find last tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } - else + if (funcid=='W') { - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + withdrawtxid=lasttxid; + if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) { - tmptxid = it->first.txhash; - if (GetTransaction(tmptxid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && DecodeGatewaysPartialOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,K,signerpk,refcoin,hex) == 'P' && K>maxK ) - { - maxK=K; - parttxid=tmptxid; - } + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } - if (maxK>0) + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) { - if (AddNormalinputs(mtx,mypk,txfee,3)==0) fprintf(stderr,"error adding funds for partialsign\n"); - mtx.vin.push_back(CTxIn(parttxid,0,CScript())); + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } - else fprintf(stderr,"Error finding previous partial tx\n"); } - - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,5000,txidaddrpk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysPartialOpRet('P',maxK+1,mypk,refcoin,hex))); + else if (funcid=='P') + { + if (DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + { + CCerror = strprintf("cannot decode partialsign tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tmptx,hashBlock,false)==0 || (numvouts= tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find withdraw tx %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin) + { + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(tx.GetHash(),0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysPartialOpRet('P',withdrawtxid,refcoin,K+1,mypk,hex))); + } + CCerror = strprintf("error adding funds for partialsign"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } -std::string GatewaysCompleteSigning(uint64_t txfee,uint256 withdrawtxid,std::string refcoin,std::string hex) +std::string GatewaysCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C; char txidaddr[65]; + CPubKey mypk,gatewayspk,signerpk,withdrawpub; struct CCcontract_info *cp,C; char funcid,str[65],depositaddr[64]; int64_t amount,totalsupply; + std::string coin,tmphex; CTransaction tx,tmptx; uint256 withdrawtxid,hashBlock,tokenid,tmptokenid,bindtxid,oracletxid; int32_t numvouts; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + cp = CCinit(&C,EVAL_GATEWAYS); mypk = pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); if ( txfee == 0 ) - txfee = 10000; - - mtx.vin.push_back(CTxIn(withdrawtxid,2,CScript())); - mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,txfee,gatewayspk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysCompleteSigningOpRet('C',refcoin,withdrawtxid,hex))); + txfee = 10000; + if (GetTransaction(lasttxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + { + CCerror = strprintf("invalid last txid %s",uint256_str(str,lasttxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (funcid=='W') + { + withdrawtxid=lasttxid; + if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + else if (funcid=='P') + { + if (DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + { + CCerror = strprintf("cannot decode partialsign tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())==0) + { + CCerror = strprintf("invalid withdraw txid %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysBindOpRet(depositaddr,tmptx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(lasttxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_GATEWAYS,CC_MARKER_VALUE,gatewayspk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysCompleteSigningOpRet('S',withdrawtxid,refcoin,K+1,hex))); + } + CCerror = strprintf("error adding funds for completesigning"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); } std::string GatewaysMarkDone(uint64_t txfee,uint256 completetxid,std::string refcoin) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C; char txidaddr[65]; + CPubKey mypk; struct CCcontract_info *cp,C; char str[65],depositaddr[64]; CTransaction tx; int32_t numvouts; + uint256 withdrawtxid,bindtxid,oracletxid,tokenid,tmptokenid,hashBlock; std::string coin,hex; + uint8_t K,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; int64_t amount,totalsupply; CPubKey withdrawpub; + cp = CCinit(&C,EVAL_GATEWAYS); mypk = pubkey2pk(Mypubkey()); if ( txfee == 0 ) txfee = 10000; - mtx.vin.push_back(CTxIn(completetxid,0,CScript())); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysMarkDoneOpRet('M',refcoin,completetxid))); + if (GetTransaction(completetxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0) + { + CCerror = strprintf("invalid completesigning txid %s",uint256_str(str,completetxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex)!='S' || refcoin!=coin) + { + CCerror = strprintf("cannot decode completesigning tx opret %s",uint256_str(str,completetxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(completetxid)==false) + { + CCerror = strprintf("gatewayscompletesigning tx not yet confirmed/notarized"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())==0) + { + CCerror = strprintf("invalid withdraw txid %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s\n",uint256_str(str,withdrawtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin || tokenid!=tmptokenid) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(completetxid,0,CScript())); + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysMarkDoneOpRet('M',withdrawtxid,refcoin,completetxid))); + } + CCerror = strprintf("error adding funds for markdone"); + LOGSTREAM("gatewayscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +UniValue GatewaysPendingDeposits(uint256 bindtxid,std::string refcoin) +{ + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex,pub; + CPubKey mypk,gatewayspk,destpub; std::vector pubkeys,publishers; std::vector txids; + uint256 tmpbindtxid,hashBlock,txid,tokenid,oracletxid,cointxid; uint8_t M,N,taddr,prefix,prefix2,wiftype; + char depositaddr[65],coinaddr[65],str[65],destaddr[65],txidaddr[65]; std::vector proof; + int32_t numvouts,vout,claimvout,height; int64_t totalsupply,nValue,amount; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_GATEWAYS); + mypk = pubkey2pk(Mypubkey()); + gatewayspk = GetUnspendable(cp,0); + _GetCCaddress(coinaddr,EVAL_GATEWAYS,mypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); + } + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); + } + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + DecodeGatewaysDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,publishers,txids,height,cointxid,claimvout,hex,proof,destpub,amount) == 'D' + && tmpbindtxid==bindtxid && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("cointxid",uint256_str(str,cointxid))); + obj.push_back(Pair("deposittxid",uint256_str(str,txid))); + CCtxidaddr(txidaddr,txid); + obj.push_back(Pair("deposittxidaddr",txidaddr)); + _GetCCaddress(destaddr,EVAL_TOKENS,destpub); + obj.push_back(Pair("depositaddr",depositaddr)); + obj.push_back(Pair("tokens_destination_address",destaddr)); + pub=HexStr(destpub); + obj.push_back(Pair("claim_pubkey",pub)); + obj.push_back(Pair("amount",(double)amount/COIN)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + pending.push_back(obj); + } + } + result.push_back(Pair("coin",refcoin)); + result.push_back(Pair("pending",pending)); + return(result); } UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) { - UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string tmprefcoin; CPubKey mypk,gatewayspk,withdrawpub; std::vector msigpubkeys; - uint256 hashBlock,tokenid,txid,oracletxid; uint8_t M,N,taddr,prefix,prefix2; - char depositaddr[64],coinaddr[64],destaddr[64],str[65],withaddr[64],numstr[32],txidaddr[64],cctxidaddr[64],signeraddr[64]; + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex; CPubKey mypk,gatewayspk,withdrawpub,signerpk; + std::vector msigpubkeys; uint256 hashBlock,tokenid,txid,tmpbindtxid,tmptokenid,oracletxid,withdrawtxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char funcid,depositaddr[65],coinaddr[65],tokensaddr[65],destaddr[65],str[65],withaddr[65],numstr[32],signeraddr[65],txidaddr[65]; int32_t i,n,numvouts,vout,queueflag; int64_t totalsupply,amount,nValue; struct CCcontract_info *cp,C; std::vector > unspentOutputs; @@ -1125,14 +1542,17 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) mypk = pubkey2pk(Mypubkey()); gatewayspk = GetUnspendable(cp,0); _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); + GetTokensCCaddress(cp,tokensaddr,gatewayspk); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); return(result); } - if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmprefcoin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || tmprefcoin != refcoin ) + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) { - fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),tmprefcoin.c_str()); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); return(result); } n = msigpubkeys.size(); @@ -1143,34 +1563,51 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) queueflag = 1; break; } - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; nValue = (int64_t)it->second.satoshis; - fprintf(stderr,"%s %d %ld\n",txid.ToString().c_str(),vout,(long)nValue); - if ( vout == 2 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && - DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,withdrawpub,amount) == 'W' && myIsutxo_spentinmempool(txid,vout) == 0) + K=0; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P') && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) { - Getscriptaddress(destaddr,tx.vout[0].scriptPubKey); - Getscriptaddress(withaddr,tx.vout[1].scriptPubKey); - if ( strcmp(destaddr,coinaddr) == 0 ) + if (funcid=='W') + { + if (DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,amount)==0 || refcoin!=coin || tmptokenid!=tokenid || tmpbindtxid!=bindtxid) continue; + } + else if (funcid=='P') + { + if (DecodeGatewaysPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)!='P' || GetTransaction(withdrawtxid,tx,hashBlock,false)==0 + || (numvouts=tx.vout.size())<=0 || DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,tmpbindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin || tmptokenid!=tokenid || tmpbindtxid!=bindtxid) + continue; + } + Getscriptaddress(destaddr,tx.vout[1].scriptPubKey); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + if ( strcmp(destaddr,tokensaddr) == 0 ) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid",uint256_str(str,txid))); - _GetCCaddress(cctxidaddr,EVAL_GATEWAYS,CCtxidaddr(txidaddr,txid)); - obj.push_back(Pair("txidaddr",cctxidaddr)); + obj.push_back(Pair("withdrawtxid",uint256_str(str,tx.GetHash()))); + CCCustomtxidaddr(txidaddr,tx.GetHash(),taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); obj.push_back(Pair("withdrawaddr",withaddr)); - sprintf(numstr,"%.8f",(double)tx.vout[0].nValue/COIN); - obj.push_back(Pair("amount",numstr)); - obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(tx.GetHash()))); if ( queueflag != 0 ) { obj.push_back(Pair("depositaddr",depositaddr)); - Getscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); + GetCustomscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG,taddr,prefix,prefix2); obj.push_back(Pair("signeraddr",signeraddr)); } + if (N>1) + { + obj.push_back(Pair("number_of_signs",K)); + obj.push_back(Pair("last_txid",uint256_str(str,txid))); + if (K>0) obj.push_back(Pair("hex",hex)); + } pending.push_back(obj); } } @@ -1183,9 +1620,10 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin) UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin) { - UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx; std::string tmprefcoin,hex; CPubKey mypk,gatewayspk,withdrawpub; std::vector msigpubkeys; - uint256 withdrawtxid,hashBlock,txid,tokenid,oracletxid; uint8_t M,N,taddr,prefix,prefix2; - char depositaddr[64],coinaddr[64],str[65],numstr[32],txidaddr[64],cctxidaddr[64],withaddr[64]; + UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx; std::string coin,hex; + CPubKey mypk,gatewayspk,withdrawpub; std::vector msigpubkeys; + uint256 withdrawtxid,hashBlock,txid,tokenid,tmptokenid,oracletxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char depositaddr[65],coinaddr[65],str[65],numstr[32],withaddr[65],txidaddr[65]; int32_t i,n,numvouts,vout,queueflag; int64_t totalsupply,nValue,amount; struct CCcontract_info *cp,C; std::vector > unspentOutputs; @@ -1195,12 +1633,14 @@ UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin) _GetCCaddress(coinaddr,EVAL_GATEWAYS,gatewayspk); if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) { - fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid)); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); return(result); } - if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tmprefcoin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || tmprefcoin != refcoin ) + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) { - fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),tmprefcoin.c_str()); + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); return(result); } n = msigpubkeys.size(); @@ -1211,26 +1651,28 @@ UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin) queueflag = 1; break; } - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; nValue = (int64_t)it->second.satoshis; - //fprintf(stderr,"%s %d %ld\n",txid.ToString().c_str(),vout,(long)nValue); if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && - DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,tmprefcoin,withdrawtxid,hex) == 'C' && myIsutxo_spentinmempool(txid,vout) == 0) + DecodeGatewaysCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex) == 'S' && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) { - if (GetTransaction(withdrawtxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmprefcoin,withdrawpub,amount) == 'W') + if (GetTransaction(withdrawtxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 + && DecodeGatewaysWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmptokenid,bindtxid,coin,withdrawpub,amount) == 'W' || refcoin!=coin || tmptokenid!=tokenid) { UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("txid",uint256_str(str,txid))); - obj.push_back(Pair("withdrawtxid",uint256_str(str,withdrawtxid))); - _GetCCaddress(cctxidaddr,EVAL_GATEWAYS,CCtxidaddr(txidaddr,withdrawtxid)); - obj.push_back(Pair("withdrawtxidaddr",cctxidaddr)); - Getscriptaddress(withaddr,tx.vout[1].scriptPubKey); + obj.push_back(Pair("completesigningtxid",uint256_str(str,txid))); + obj.push_back(Pair("withdrawtxid",uint256_str(str,withdrawtxid))); + CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); obj.push_back(Pair("withdrawaddr",withaddr)); obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); obj.push_back(Pair("hex",hex)); processed.push_back(obj); } @@ -1242,51 +1684,17 @@ UniValue GatewaysProcessedWithdraws(uint256 bindtxid,std::string refcoin) return(result); } -UniValue GatewaysMultisig(char *cctxidaddr) -{ - std::string parthex,hex,refcoin; uint256 txid,hashBlock; CTransaction tx; int32_t i,maxK,K,numvouts; CPubKey signerpk; - std::vector > unspentOutputs; UniValue result(UniValue::VOBJ); - - SetCCunspents(unspentOutputs,cctxidaddr); - maxK=0; - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - if (GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size()) > 0 && DecodeGatewaysPartialOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,K,signerpk,refcoin,hex) == 'P' && K>maxK ) - { - maxK=K; - parthex=hex; - } - } - - BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) - { - const CTransaction &txmempool = e.GetTx(); - const uint256 &hash = txmempool.GetHash(); - - if ((numvouts=txmempool.vout.size()) > 0 && DecodeGatewaysPartialOpRet(txmempool.vout[numvouts-1].scriptPubKey,K,signerpk,refcoin,hex) == 'P' && K>maxK) - { - maxK=K; - parthex=hex; - } - } - - result.push_back(Pair("hex",parthex)); - result.push_back(Pair("number_of_signs",maxK)); - return (result); -} - UniValue GatewaysList() { - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid,tokenid; CTransaction vintx; std::string coin; int64_t totalsupply; char str[65],depositaddr[64]; uint8_t M,N,taddr,prefix,prefix2; std::vector pubkeys; + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid,tokenid; CTransaction vintx; std::string coin; int64_t totalsupply; char str[65],depositaddr[64]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; cp = CCinit(&C,EVAL_GATEWAYS); - SetCCtxids(addressIndex,cp->unspendableCCaddr); + SetCCtxids(addressIndex,cp->unspendableCCaddr,true); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( vintx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 0 ) + if ( vintx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 ) { result.push_back(uint256_str(str,txid)); } @@ -1295,38 +1703,100 @@ UniValue GatewaysList() return(result); } +UniValue GatewaysExternalAddress(uint256 bindtxid,CPubKey pubkey) +{ + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid,tokenid; CTransaction tx; + std::string coin; int64_t numvouts,totalsupply; char str[65],addr[65],depositaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_GATEWAYS); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); + } + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); + } + GetCustomscriptaddress(addr,CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG,taddr,prefix,prefix2); + result.push_back(Pair("result","success")); + result.push_back(Pair("address",addr)); + return(result); +} + +UniValue GatewaysDumpPrivKey(uint256 bindtxid,CKey key) +{ + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid,tokenid; CTransaction tx; + std::string coin,priv; int64_t numvouts,totalsupply; char str[65],addr[65],depositaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_GATEWAYS); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); + } + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); + } + + priv=EncodeCustomSecret(key,wiftype); + result.push_back(Pair("result","success")); + result.push_back(Pair("privkey",priv.c_str())); + return(result); +} + UniValue GatewaysInfo(uint256 bindtxid) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; char str[67],numstr[65],depositaddr[64],gatewaysassets[64]; uint8_t M,N; std::vector pubkeys; uint8_t taddr,prefix,prefix2; uint256 tokenid,oracletxid,hashBlock; CTransaction tx; CPubKey Gatewayspk; struct CCcontract_info *cp,C; int32_t i; int64_t totalsupply,remaining; - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Gateways")); + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; char str[67],numstr[65],depositaddr[64],gatewaystokens[64]; + uint8_t M,N; std::vector pubkeys; uint8_t taddr,prefix,prefix2,wiftype; uint256 tokenid,oracletxid,hashBlock; CTransaction tx; + CPubKey Gatewayspk; struct CCcontract_info *cp,C; int32_t i; int64_t numvouts,totalsupply,remaining; std::vector msigpubkeys; + cp = CCinit(&C,EVAL_GATEWAYS); Gatewayspk = GetUnspendable(cp,0); - _GetCCaddress(gatewaysassets,EVAL_GATEWAYS,Gatewayspk); + GetTokensCCaddress(cp,gatewaystokens,Gatewayspk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)))); + return(result); + } + if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error",strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()))); + return(result); + } if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 ) { + result.push_back(Pair("result","success")); + result.push_back(Pair("name","Gateways")); depositaddr[0] = 0; - if ( tx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,tx.vout[tx.vout.size()-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 0 && M <= N && N > 0 ) + if ( tx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,tx.vout[tx.vout.size()-1].scriptPubKey,tokenid,coin,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 && M <= N && N > 0 ) { - if ( N > 1 ) - { - result.push_back(Pair("M",M)); - result.push_back(Pair("N",N)); - for (i=0; i int64_t IsHeirFundingVout(struct CCcontract_info* cp, co } // makes coin initial tx opret -CScript EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) +vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { uint8_t evalcode = EVAL_HEIR; - return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName); + return /*CScript() << OP_RETURN <<*/ E_MARSHAL(ss << evalcode << funcid << ownerPubkey << heirPubkey << inactivityTimeSec << heirName << memo); } // makes coin additional tx opret -CScript EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpendingBegun) +vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t hasHeirSpendingBegun) { uint8_t evalcode = EVAL_HEIR; fundingtxid = revuint256(fundingtxid); - return CScript() << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); + return /*CScript() << OP_RETURN <<*/ E_MARSHAL(ss << evalcode << funcid << fundingtxid << hasHeirSpendingBegun); } // decode opret vout for Heir contract -uint8_t _DecodeHeirOpRet(std::vector vopret, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) +uint8_t _DecodeHeirOpRet(vscript_t vopret, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { uint8_t evalCodeInOpret = 0; uint8_t heirFuncId = 0; @@ -287,13 +287,13 @@ uint8_t _DecodeHeirOpRet(std::vector vopret, CPubKey& ownerPubkey, CPub uint8_t heirFuncId = 0; hasHeirSpendingBegun = 0; - bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; \ - if (heirFuncId == 'F') { \ - ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; \ - } \ - else { \ - ss >> fundingTxidInOpret >> hasHeirSpendingBegun; \ - } \ + bool result = E_UNMARSHAL(vopret, { ss >> evalCodeInOpret; ss >> heirFuncId; + if (heirFuncId == 'F') { + ss >> ownerPubkey; ss >> heirPubkey; ss >> inactivityTime; ss >> heirName; ss >> memo; + } + else { + ss >> fundingTxidInOpret >> hasHeirSpendingBegun; + } }); if (!result) { @@ -322,84 +322,52 @@ uint8_t _DecodeHeirOpRet(std::vector vopret, CPubKey& ownerPubkey, CPub return (uint8_t)0; } -/* not used, see DecodeHeirOpRet(vopret,...) - // overload for 'F' opret - uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) - { - uint256 dummytxid; - uint8_t dummyHasHeirSpendingBegun; - std::vector vopret; - - GetOpReturnData(scriptPubKey, vopret); - if (vopret.size() == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; - return (uint8_t)0; - } - return _DecodeHeirOpRet(vopret, ownerPubkey, heirPubkey, inactivityTime, heirName, dummytxid, dummyHasHeirSpendingBegun, noLogging); - }*/ - - -/* not used, see DecodeHeirOpRet(vopret,...) - // overload for A, C oprets and AddHeirContractInputs - uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) - { - CPubKey dummyOwnerPubkey, dummyHeirPubkey; - int64_t dummyInactivityTime; - std::string dummyHeirName; - std::vector vopret; - - GetOpReturnData(scriptPubKey, vopret); - if (vopret.size() == 0) { - if (!noLogging) std::cerr << "DecodeHeirOpRet() warning: empty opret" << std::endl; - return (uint8_t)0; - } - - return _DecodeHeirOpRet(vopret, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingtxidInOpret, hasHeirSpendingBegun, noLogging); - } */ - // decode combined opret: -uint8_t _DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) +uint8_t _DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint256& fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { - uint8_t evalCodeTokens = 0; - std::vector voutPubkeysDummy; - std::vector vopretExtra, vopretStripped; - - if (DecodeTokenOpRet(scriptPubKey, evalCodeTokens, tokenid, voutPubkeysDummy, vopretExtra) != 0) { - if (vopretExtra.size() > 1) { + uint8_t evalCodeTokens = 0; + std::vector voutPubkeysDummy; + std::vector> oprets; + vscript_t vopretExtra /*, vopretStripped*/; + + + if (DecodeTokenOpRet(scriptPubKey, evalCodeTokens, tokenid, voutPubkeysDummy, oprets) != 0 && GetOpretBlob(oprets, OPRETID_HEIRDATA, vopretExtra)) { + /* if (vopretExtra.size() > 1) { // restore the second opret: - + + /* unmarshalled in DecodeTokenOpRet: if (!E_UNMARSHAL(vopretExtra, { ss >> vopretStripped; })) { //strip string size if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() could not unmarshal vopretStripped" << std::endl; return (uint8_t)0; } - } - else { - if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; - return (uint8_t)0; - } - } - else - GetOpReturnData(scriptPubKey, vopretStripped); - - return _DecodeHeirOpRet(vopretStripped, ownerPubkey, heirPubkey, inactivityTime, heirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); + } */ + if (vopretExtra.size() < 1) { + if (!noLogging) std::cerr << "_DecodeHeirEitherOpret() empty vopretExtra" << std::endl; + return (uint8_t)0; + } + } + else { + GetOpReturnData(scriptPubKey, vopretExtra); + } + return _DecodeHeirOpRet(vopretExtra, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); } // overload to decode opret in fundingtxid: -uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging) { +uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging) { uint256 dummyFundingTxidInOpret; uint8_t dummyHasHeirSpendingBegun; - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, dummyFundingTxidInOpret, dummyHasHeirSpendingBegun, noLogging); } // overload to decode opret in A and C heir tx: uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging) { CPubKey dummyOwnerPubkey, dummyHeirPubkey; int64_t dummyInactivityTime; - std::string dummyHeirName; + std::string dummyHeirName, dummyMemo; - return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); + return _DecodeHeirEitherOpRet(scriptPubKey, tokenid, dummyOwnerPubkey, dummyHeirPubkey, dummyInactivityTime, dummyHeirName, dummyMemo, fundingTxidInOpret, hasHeirSpendingBegun, noLogging); } // check if pubkey is in vins @@ -423,7 +391,7 @@ void CheckVinPubkey(std::vector vins, CPubKey pubkey, bool &hasPubkey, bo * find the latest funding tx: it may be the first F tx or one of A or C tx's * Note: this function is also called from validation code (use non-locking calls) */ -uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, CScript& fundingOpretScript, uint8_t &hasHeirSpendingBegun) +uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, CScript& fundingOpretScript, uint8_t &hasHeirSpendingBegun) { CTransaction fundingtx; uint256 hashBlock; @@ -431,7 +399,7 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke //char markeraddr[64]; //CCtxidaddr(markeraddr, fundingtxid); - //SetCCunspents(unspentOutputs, markeraddr); + //SetCCunspents(unspentOutputs, markeraddr,true); hasHeirSpendingBegun = 0; funcId = 0; @@ -440,7 +408,7 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { CScript heirScript = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + uint8_t funcId = DecodeHeirEitherOpRet(heirScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId != 0) { // found at least funding tx! //std::cerr << "FindLatestFundingTx() lasttx currently is fundingtx, txid=" << fundingtxid.GetHex() << " opreturn type=" << (char)funcId << '\n'; @@ -461,7 +429,7 @@ uint256 _FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &toke char coinaddr[64]; GetCCaddress1of2(cp, coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' - SetCCunspents(unspentOutputs, coinaddr); // get vector with tx's with unspent vouts of 1of2pubkey address: + SetCCunspents(unspentOutputs, coinaddr,true); // get vector with tx's with unspent vouts of 1of2pubkey address: //std::cerr << "FindLatestFundingTx() using 1of2address=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; int32_t maxBlockHeight = 0; // max block height @@ -522,17 +490,17 @@ uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRe CPubKey ownerPubkey; CPubKey heirPubkey; int64_t inactivityTime; - std::string heirName; + std::string heirName, memo; - return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, opRetScript, hasHeirSpendingBegun); + return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, opRetScript, hasHeirSpendingBegun); } // overload for transaction creation code -uint256 FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, uint8_t &hasHeirSpendingBegun) +uint256 FindLatestFundingTx(uint256 fundingtxid, uint8_t& funcId, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, uint8_t &hasHeirSpendingBegun) { CScript opRetScript; - return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, opRetScript, hasHeirSpendingBegun); + return _FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, opRetScript, hasHeirSpendingBegun); } // add inputs of 1 of 2 cc address @@ -546,11 +514,11 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, char coinaddr[64]; Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get address of cryptocondition '1 of 2 pubkeys' - SetCCunspents(unspentOutputs, coinaddr); + SetCCunspents(unspentOutputs, coinaddr,true); // char markeraddr[64]; // CCtxidaddr(markeraddr, fundingtxid); - // SetCCunspents(unspentOutputs, markeraddr); + // SetCCunspents(unspentOutputs, markeraddr,true); std::cerr << "Add1of2AddressInputs() using 1of2addr=" << coinaddr << " unspentOutputs.size()=" << unspentOutputs.size() << std::endl; @@ -561,7 +529,7 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, // no need to prevent dup // dimxy: maybe it is good to put tx's in cache? - std::cerr << "Add1of2AddressInputs() txid=" << txid.GetHex() << std::endl; + //std::cerr << "Add1of2AddressInputs() txid=" << txid.GetHex() << std::endl; if (GetTransaction(txid, heirtx, hashBlock, false) != 0) { uint256 tokenid; @@ -576,7 +544,7 @@ template int64_t Add1of2AddressInputs(struct CCcontract_info* cp, isMyFuncId(funcId) && (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, voutIndex, tokenid) > 0) && // token validation logic //(voutValue = IsHeirFundingVout(cp, heirtx, voutIndex, ownerPubkey, heirPubkey)) > 0 && // heir contract vout validation logic - not used since we moved to 2-eval vouts - !myIsutxo_spentinmempool(txid, voutIndex)) + !myIsutxo_spentinmempool(ignoretxid,ignorevin,txid, voutIndex)) { std::cerr << "Add1of2AddressInputs() satoshis=" << it->second.satoshis << std::endl; if (total != 0 && maxinputs != 0) @@ -601,7 +569,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info Helper::GetCoinsOrTokensCCaddress1of2(coinaddr, ownerPubkey, heirPubkey); // get the address of cryptocondition '1 of 2 pubkeys' std::vector> addressIndexes; - SetCCtxids(addressIndexes, coinaddr); + SetCCtxids(addressIndexes, coinaddr,true); //fprintf(stderr,"LifetimeHeirContractFunds() scan lifetime of %s\n",coinaddr); int64_t total = 0; @@ -626,7 +594,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info (txid == fundingtxid || fundingTxidInOpret == fundingtxid) && isMyFuncId(funcId) && !isSpendingTx(funcId) && (typeid(Helper) != typeid(TokenHelper) || IsTokensvout(true, true, cp, nullptr, heirtx, ivout, tokenid) > 0) && - !myIsutxo_spentinmempool(txid, ivout)) // exclude tx in mempool + !myIsutxo_spentinmempool(ignoretxid,ignorevin,txid, ivout)) // exclude tx in mempool { total += it->second; // dont do this: tx.vout[ivout].nValue; // in vin[0] always is the pay to 1of2 addr (funding or change) //std::cerr << "LifetimeHeirContractFunds() added tx=" << txid.GetHex() << " it->second=" << it->second << " vout[0].nValue=" << tx.vout[ivout].nValue << " opreturn=" << (char)funcId << '\n'; @@ -644,7 +612,7 @@ template int64_t LifetimeHeirContractFunds(struct CCcontract_info * and also for setting spending plan for the funds' owner and heir * @return fundingtxid handle for subsequent references to this heir funding plan */ -template UniValue _HeirFund(int64_t txfee, int64_t amount, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) +template UniValue _HeirFund(int64_t txfee, int64_t amount, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string memo, uint256 tokenid) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -657,13 +625,7 @@ template UniValue _HeirFund(int64_t txfee, int64_t amount, std int64_t markerfee = 10000; //std::cerr << "HeirFund() amount=" << amount << " txfee=" << txfee << " heirPubkey IsValid()=" << heirPubkey.IsValid() << " inactivityTime(sec)=" << inactivityTimeSec << " tokenid=" << tokenid.GetHex() << std::endl; - - if (!heirPubkey.IsValid()) { - std::cerr << "HeirFund() heirPubkey is not valid!" << std::endl; - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "invalid heir pubkey")); - } - + CPubKey myPubkey = pubkey2pk(Mypubkey()); if (AddNormalinputs(mtx, myPubkey, markerfee, 3) > 0) { @@ -711,10 +673,10 @@ template UniValue _HeirFund(int64_t txfee, int64_t amount, std // add change for txfee and opreturn vouts and sign tx: std::string rawhextx = FinalizeCCTx(0, cp, mtx, myPubkey, txfee, - Helper::makeCreateOpRet(tokenid, voutTokenPubkeys, myPubkey, heirPubkey, inactivityTimeSec, heirName)); + Helper::makeCreateOpRet(tokenid, voutTokenPubkeys, myPubkey, heirPubkey, inactivityTimeSec, heirName, memo)); if (!rawhextx.empty()) { result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); + result.push_back(Pair("hex", rawhextx)); } else { std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; @@ -737,12 +699,12 @@ template UniValue _HeirFund(int64_t txfee, int64_t amount, std } // if no these callers - it could not link -UniValue HeirFundCoinCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid){ - return _HeirFund(txfee, satoshis, heirName, heirPubkey, inactivityTimeSec, tokenid); +UniValue HeirFundCoinCaller(int64_t txfee, int64_t coins, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string memo){ + return _HeirFund(txfee, coins, heirName, heirPubkey, inactivityTimeSec, memo, zeroid); } -UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, uint256 tokenid) { - return _HeirFund(txfee, satoshis, heirName, heirPubkey, inactivityTimeSec, tokenid); +UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirName, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string memo, uint256 tokenid) { + return _HeirFund(txfee, satoshis, heirName, heirPubkey, inactivityTimeSec, memo, tokenid); } /** @@ -750,7 +712,7 @@ UniValue HeirFundTokenCaller(int64_t txfee, int64_t satoshis, std::string heirNa * creates tx to add more funds to cryptocondition address for spending by either funds' owner or heir * @return result object with raw tx or error text */ -template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) +template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, uint8_t hasHeirSpendingBegun) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -835,7 +797,7 @@ template UniValue _HeirAdd(uint256 fundingtxid, int64_t txfee, in Helper::makeAddOpRet(tokenid, voutTokenPubkeys, fundingtxid, hasHeirSpendingBegun))); if (!rawhextx.empty()) { - result.push_back(Pair("hextx", rawhextx)); + result.push_back(Pair("hex", rawhextx)); } else { std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; @@ -868,10 +830,10 @@ UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount uint256 latesttxid, tokenid = zeroid; uint8_t funcId; - std::string heirName; + std::string heirName, memo; uint8_t hasHeirSpendingBegun = 0; - if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { + if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo, hasHeirSpendingBegun)) != zeroid) { if (tokenid == zeroid) { int64_t amount = (int64_t)(atof(strAmount.c_str()) * COIN); if (amount <= 0) { @@ -880,8 +842,7 @@ UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount result.push_back(Pair("error", "invalid amount")); return result; } - - return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, hasHeirSpendingBegun); } else { int64_t amount = atoll(strAmount.c_str()); @@ -891,7 +852,7 @@ UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount result.push_back(Pair("error", "invalid amount")); return result; } - return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + return _HeirAdd(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, hasHeirSpendingBegun); } } else { @@ -910,7 +871,7 @@ UniValue HeirAddCaller(uint256 fundingtxid, int64_t txfee, std::string strAmount * creates tx to spend funds from cryptocondition address by either funds' owner or heir * @return result object with raw tx or error text */ -template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, uint8_t hasHeirSpendingBegun) +template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee, int64_t amount, uint256 latesttxid, uint8_t funcId, uint256 tokenid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, uint8_t hasHeirSpendingBegun) { UniValue result(UniValue::VOBJ); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -1008,7 +969,7 @@ template UniValue _HeirClaim(uint256 fundingtxid, int64_t txfee if (!rawhextx.empty()) { result.push_back(Pair("result", "success")); - result.push_back(Pair("hextx", rawhextx)); + result.push_back(Pair("hex", rawhextx)); } else { std::cerr << "HeirAdd error in FinalizeCCtx" << std::endl; @@ -1038,10 +999,10 @@ UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string strAmou uint256 latesttxid, tokenid = zeroid; uint8_t funcId; - std::string heirName; + std::string heirName, memo; uint8_t hasHeirSpendingBegun = 0; - if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun)) != zeroid) { + if ((latesttxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo, hasHeirSpendingBegun)) != zeroid) { if (tokenid == zeroid) { int64_t amount = (int64_t)(atof(strAmount.c_str()) * COIN); if (amount < 0) { @@ -1050,7 +1011,7 @@ UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string strAmou result.push_back(Pair("error", "invalid amount")); return result; } - return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, hasHeirSpendingBegun); } else { int64_t amount = atoll(strAmount.c_str()); @@ -1060,7 +1021,7 @@ UniValue HeirClaimCaller(uint256 fundingtxid, int64_t txfee, std::string strAmou result.push_back(Pair("error", "invalid amount")); return result; } - return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + return _HeirClaim(fundingtxid, txfee, amount, latesttxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, hasHeirSpendingBegun); } } @@ -1092,14 +1053,14 @@ UniValue HeirInfo(uint256 fundingtxid) //char markeraddr[64]; //CCtxidaddr(markeraddr, fundingtxid); - //SetCCunspents(unspentOutputs, markeraddr); + //SetCCunspents(unspentOutputs, markeraddr,true); // get initial funding tx and set it as initial lasttx: if (myGetTransaction(fundingtxid, fundingtx, hashBlock) && fundingtx.vout.size()) { CPubKey ownerPubkey, heirPubkey; uint256 dummyTokenid, tokenid = zeroid; // important to clear tokenid - std::string heirName; + std::string heirName, memo; int64_t inactivityTimeSec; const bool noLogging = false; uint8_t funcId; @@ -1118,7 +1079,7 @@ UniValue HeirInfo(uint256 fundingtxid) uint8_t hasHeirSpendingBegun = 0; - uint256 latestFundingTxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, hasHeirSpendingBegun); + uint256 latestFundingTxid = FindLatestFundingTx(fundingtxid, funcId, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo, hasHeirSpendingBegun); if (latestFundingTxid != zeroid) { int32_t numblocks; @@ -1159,12 +1120,22 @@ UniValue HeirInfo(uint256 fundingtxid) else total = LifetimeHeirContractFunds(cp, fundingtxid, ownerPubkey, heirPubkey); + msg = "type"; + if (tokenid == zeroid) { + stream << "coins"; + } + else { + stream << "tokens"; + } + result.push_back(Pair(msg, stream.str().c_str())); + stream.str(""); + stream.clear(); + + msg = "lifetime"; if (tokenid == zeroid) { - msg = "funding total in coins"; stream << std::fixed << std::setprecision(8) << (double)total / COIN; } else { - msg = "funding total in tokens"; stream << total; } result.push_back(Pair(msg, stream.str().c_str())); @@ -1177,12 +1148,11 @@ UniValue HeirInfo(uint256 fundingtxid) else inputs = Add1of2AddressInputs(cp, fundingtxid, mtx, ownerPubkey, heirPubkey, 0, 60); + msg = "available"; if (tokenid == zeroid) { - msg = "funding available in coins"; stream << std::fixed << std::setprecision(8) << (double)inputs / COIN; } else { - msg = "funding available in tokens"; stream << inputs; } result.push_back(Pair(msg, stream.str().c_str())); @@ -1192,14 +1162,14 @@ UniValue HeirInfo(uint256 fundingtxid) if (tokenid != zeroid) { int64_t ownerInputs = TokenHelper::addOwnerInputs(tokenid, mtx, ownerPubkey, 0, (int32_t)64); stream << ownerInputs; - msg = "owner funding available in tokens"; + msg = "OwnerRemainderTokens"; result.push_back(Pair(msg, stream.str().c_str())); stream.str(""); stream.clear(); } stream << inactivityTimeSec; - result.push_back(Pair("inactivity time setting, sec", stream.str().c_str())); + result.push_back(Pair("InactivityTimeSetting", stream.str().c_str())); stream.str(""); stream.clear(); @@ -1209,18 +1179,20 @@ UniValue HeirInfo(uint256 fundingtxid) } stream << std::boolalpha << (hasHeirSpendingBegun || durationSec > inactivityTimeSec); - result.push_back(Pair("spending allowed for the heir", stream.str().c_str())); + result.push_back(Pair("IsHeirSpendingAllowed", stream.str().c_str())); stream.str(""); stream.clear(); // adding owner current inactivity time: if (!hasHeirSpendingBegun && durationSec <= inactivityTimeSec) { stream << durationSec; - result.push_back(Pair("owner inactivity time, sec", stream.str().c_str())); + result.push_back(Pair("InactivityTime", stream.str().c_str())); stream.str(""); stream.clear(); } - + + result.push_back(Pair("memo", memo.c_str())); + result.push_back(Pair("result", "success")); } else { @@ -1246,7 +1218,7 @@ void _HeirList(struct CCcontract_info *cp, UniValue &result) char markeraddr[64]; GetCCaddress(cp, markeraddr, GetUnspendable(cp, NULL)); - SetCCunspents(unspentOutputs, markeraddr); + SetCCunspents(unspentOutputs, markeraddr,true); //std::cerr << "HeirList() finding heir marker from unspendable addr=" << markeraddr << " unspentOutputs.size()=" << unspentOutputs.size() << '\n'; @@ -1262,18 +1234,18 @@ void _HeirList(struct CCcontract_info *cp, UniValue &result) CTransaction fundingtx; if (GetTransaction(txid, fundingtx, hashBlock, false)) { CPubKey ownerPubkey, heirPubkey; - std::string heirName; + std::string heirName, memo; int64_t inactivityTimeSec; const bool noLogging = true; uint256 tokenid; CScript opret = (fundingtx.vout.size() > 0) ? fundingtx.vout[fundingtx.vout.size() - 1].scriptPubKey : CScript(); - uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, true); + uint8_t funcId = DecodeHeirEitherOpRet(opret, tokenid, ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo, true); // note: if it is not Heir token funcId would be equal to 0 if (funcId == 'F') { //result.push_back(Pair("fundingtxid kind name", txid.GetHex() + std::string(" ") + (typeid(Helper) == typeid(TokenHelper) ? std::string("token") : std::string("coin")) + std::string(" ") + heirName)); - result.push_back( Pair("fundingtxid", txid.GetHex()) ); + result.push_back( txid.GetHex() ); } else { std::cerr << "HeirList() this is not the initial F transaction=" << txid.GetHex() << std::endl; @@ -1288,17 +1260,14 @@ void _HeirList(struct CCcontract_info *cp, UniValue &result) UniValue HeirList() { - UniValue result(UniValue::VOBJ); - result.push_back(Pair("result", "success")); + UniValue result(UniValue::VARR); + //result.push_back(Pair("result", "success")); + //result.push_back(Pair("name", "Heir List")); - struct CCcontract_info *cpHeir, *cpTokens, heirC, tokenC; // NOTE we must use a separate 'C' structure for each CCinit! + struct CCcontract_info *cpHeir, heirC; cpHeir = CCinit(&heirC, EVAL_HEIR); - //cpTokens = CCinit(&tokenC, EVAL_TOKENS); - _HeirList(cpHeir, result); - //_HeirList(cpTokens, result); not used anymore return result; } - diff --git a/src/cc/heir_validate.h b/src/cc/heir_validate.h index df58e3bf1..a27dc3340 100644 --- a/src/cc/heir_validate.h +++ b/src/cc/heir_validate.h @@ -7,13 +7,13 @@ #define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) // makes coin initial tx opret -CScript EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName); -CScript EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan); +vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo); +vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan); uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan); //uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false); //uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false); -uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false); +uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false); uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false); inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); } @@ -28,14 +28,14 @@ public: return AddNormalinputs(mtx, ownerPubkey, total, maxinputs); } - static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { - return EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName); + static CScript makeCreateOpRet(uint256 dummyid, std::vector dummyPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { + return CScript() << OP_RETURN << EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo); } static CScript makeAddOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); + return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan); } static CScript makeClaimOpRet(uint256 dummyid, std::vector dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { - return EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); + return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { return MakeCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey); @@ -51,8 +51,11 @@ public: cpHeir = CCinit(&heirC, EVAL_HEIR); return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey); } - static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) { - CCaddr1of2set(cp, ownerPubkey, heirPubkey, coinaddr); + static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) + { + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddr1of2set(cp, ownerPubkey, heirPubkey,mypriv, coinaddr); } }; @@ -66,17 +69,17 @@ public: return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs); } - static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName) { + static CScript makeCreateOpRet(uint256 tokenid, std::vector voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) { return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName)); + std::make_pair(OPRETID_HEIRDATA, EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo))); } static CScript makeAddOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)); + std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan))); } static CScript makeClaimOpRet(uint256 tokenid, std::vector voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) { return EncodeTokenOpRet(tokenid, voutTokenPubkeys, - EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)); + std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan))); } static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) { @@ -407,10 +410,10 @@ public: //std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl; CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; - std::string heirName; + std::string heirName, memo; uint256 tokenid; - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = m_customMessage + std::string(" invalid opreturn format"); std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl; @@ -462,13 +465,13 @@ public: CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; - std::string heirName; + std::string heirName, memo; uint256 tokenid; ///std::cerr << "CMyPubkeyVoutValidator::validateVout() m_opRetScript=" << m_opRetScript.ToString() << std::endl; // get both pubkeys: - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; @@ -522,11 +525,11 @@ public: CPubKey ownerPubkey, heirPubkey; int64_t inactivityTime; - std::string heirName; + std::string heirName, memo; uint256 tokenid; // get heir pubkey: - uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, true); + uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true); if (funcId == 0) { message = std::string("invalid opreturn format"); return false; diff --git a/src/cc/import.cpp b/src/cc/import.cpp index ea69745b0..b847e07d2 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -19,77 +19,366 @@ #include "crosschain.h" #include "primitives/transaction.h" #include "cc/CCinclude.h" +#include -/* - * CC Eval method for import coin. - * - * This method should control every parameter of the ImportCoin transaction, since it has no signature - * to protect it from malleability. - - ##### 0xffffffff is a special CCid for single chain/dual daemon imports - */ +#include "key_io.h" +#define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA" extern std::string ASSETCHAINS_SELFIMPORT; extern uint16_t ASSETCHAINS_CODAPORT,ASSETCHAINS_BEAMPORT; extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33]; -int32_t GetSelfimportProof(std::string source,CMutableTransaction &mtx,CScript &scriptPubKey,TxProof &proof,uint64_t burnAmount,std::vector rawtx,uint256 txid,std::vector rawproof) // find burnTx with hash from "other" daemon +// utilities from gateways.cpp +uint256 BitcoinGetProofMerkleRoot(const std::vector &proofData, std::vector &txids); +uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid, uint256 batontxid); +int32_t GatewaysCointxidExists(struct CCcontract_info *cp, uint256 cointxid); +uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype); +char *nonportable_path(char *str); +char *portable_path(char *str); +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep); +void *filestr(long *allocsizep,char *_fname); + +// ac_import=chain support: +// encode opret for gateways import +CScript EncodeImportTxOpRet(uint32_t targetCCid, std::string coin, std::vector publishers, std::vectortxids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vectorproof, CPubKey destpub, int64_t amount) { - MerkleBranch newBranch; CMutableTransaction tmpmtx; CTransaction tx,vintx; uint256 blockHash; char destaddr[64],pkaddr[64]; - tmpmtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),komodo_nextheight()); - if ( source == "BEAM" ) + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << targetCCid << coin << publishers << txids << height << cointxid << claimvout << rawburntx << proof << destpub << amount); + return(opret); +} + +CScript EncodeGatewaysImportTxOpRet(uint32_t targetCCid, std::string coin, uint256 bindtxid, std::vector publishers, std::vectortxids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vectorproof, CPubKey destpub, int64_t amount) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << targetCCid << coin << bindtxid << publishers << txids << height << cointxid << claimvout << rawburntx << proof << destpub << amount); + return(opret); +} + +CScript EncodeCodaImportTxOpRet(uint32_t targetCCid, std::string coin, std::string burntx, uint256 bindtxid, CPubKey destpub, int64_t amount) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << targetCCid << burntx << bindtxid << destpub << amount); + return(opret); +} + +cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,char const *arg3,char const *arg4,char const *arg5) +{ + char cmdstr[5000],fname[256],*jsonstr; + long fsize; + cJSON *retjson=NULL; + + sprintf(fname,"/tmp/coda.%s",arg0); + sprintf(cmdstr,"coda.exe client %s %s %s %s %s %s > %s 2>&1",arg0,arg1,arg2,arg3,arg4,arg5,fname); + *retstr = 0; + if (system(cmdstr)<0) return (retjson); + if ( (jsonstr=(char *)filestr(&fsize,fname)) != 0 ) { - if ( ASSETCHAINS_BEAMPORT == 0 ) - return(-1); - // confirm via ASSETCHAINS_BEAMPORT that burnTx/hash is a valid BEAM burn - // return(0); + jsonstr[strlen(jsonstr)-1]='\0'; + if ( (strncmp(jsonstr,"Merkle List of transactions:",28)!=0) || (retjson= cJSON_Parse(jsonstr+29)) == 0) + *retstr=jsonstr; + else free(jsonstr); } - else if ( source == "CODA" ) + return(retjson); +} + +bool ImportCoinGatewaysVerify(CTransaction oracletx, int32_t claimvout, std::string refcoin, uint256 burntxid, const std::string rawburntx, std::vectorproof, uint256 merkleroot) +{ + std::vector txids; + uint256 proofroot; + std::string name, description, format; + int32_t i, numvouts; + + if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey, name, description, format) != 'C' || name != refcoin) { - if ( ASSETCHAINS_CODAPORT == 0 ) - return(-1); - // confirm via ASSETCHAINS_CODAPORT that burnTx/hash is a valid CODA burn - // return(0); + LOGSTREAM("importcoin", CCLOG_INFO, stream << "ImportCoinGatewaysVerify mismatched oracle name=" << name.c_str() << " != " << refcoin.c_str() << std::endl); + return false; + } + + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "verified proof for burntxid=" << burntxid.GetHex() << " in trusted merkleroot" << std::endl); + return true; +} + +// make import tx with burntx and its proof of existence +// std::string MakeGatewaysImportTx(uint64_t txfee, uint256 oracletxid, int32_t height, std::string refcoin, std::vector proof, std::string rawburntx, int32_t ivout, uint256 burntxid,std::string destaddr) +// { +// CMutableTransaction burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); +// CTransaction oracletx,regtx; CPubKey regpk; +// uint256 proofroot,txid,tmporacletxid,merkleroot,mhash,hashBlock; int32_t i,m,n=0,numvouts; +// std::string name,desc,format; std::vector vouts; +// std::vector pubkeys; std::vectortxids; +// char markeraddr[64]; int64_t datafee; +// std::vector > unspentOutputs; + +// if (!E_UNMARSHAL(ParseHex(rawburntx), ss >> burntx)) +// return std::string(""); +// CAmount amount = GetCoinImportValue(burntx); // equal to int64_t +// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx height=" << height << " coin=" << refcoin << " amount=" << (double)amount / COIN << " pubkeys num=" << pubkeys.size() << std::endl); +// if (GetTransaction(oracletxid, oracletx, hashBlock, false) == 0 || (numvouts = oracletx.vout.size()) <= 0) +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx cant find oracletxid=" << oracletxid.GetHex() << std::endl); +// return(""); +// } +// if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C') +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl); +// return(""); +// } +// if (name!=refcoin || format!="Ihh") +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl); +// return(""); +// } +// CCtxidaddr(markeraddr,oracletxid); +// SetCCunspents(unspentOutputs,markeraddr,true); +// for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) +// { +// txid = it->first.txhash; +// if ( GetTransaction(txid,regtx,hashBlock,false) != 0 && regtx.vout.size() > 0 +// && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid ) +// { +// pubkeys.push_back(regpk); +// n++; +// } +// } +// merkleroot = zeroid; +// for (i = m = 0; i < n; i++) +// { +// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx using pubkeys[" << i << "]=" << HexStr(pubkeys[i]) << std::endl); +// if ((mhash = CCOraclesReverseScan("importcoind-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid) +// { +// if (merkleroot == zeroid) +// merkleroot = mhash, m = 1; +// else if (mhash == merkleroot) +// m ++; +// txids.push_back(txid); +// } +// } +// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx burntxid=" << burntxid.GetHex() << " nodes m=" << m << " of n=" << n << std::endl); +// if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl ); +// return(""); +// } +// proofroot = BitcoinGetProofMerkleRoot(proof, txids); +// if (proofroot != merkleroot) +// { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx mismatched proof merkleroot=" << proofroot.GetHex() << " and oracles merkleroot=" << merkleroot.GetHex() << std::endl); +// return(""); +// } +// // check the burntxid is in the proof: +// if (std::find(txids.begin(), txids.end(), burntxid) == txids.end()) { +// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid proof for this burntxid=" << burntxid.GetHex() << std::endl); +// return(""); +// } +// burntx.vout.push_back(MakeBurnOutput(amount,0xffffffff,refcoin,vouts,proof,oracletxid,height)); +// std::vector leaftxids; +// BitcoinGetProofMerkleRoot(proof, leaftxids); +// MerkleBranch newBranch(0, leaftxids); +// TxProof txProof = std::make_pair(burntxid, newBranch); +// CTxDestination dest = DecodeDestination(destaddr.c_str()); +// CScript scriptPubKey = GetScriptForDestination(dest); +// vouts.push_back(CTxOut(amount,scriptPubKey)); +// return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof, burntx, vouts))); +// } + +// makes source tx for self import tx +std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx) +{ + const int64_t txfee = 10000; + int64_t inputs, change; + CPubKey myPubKey = Mypubkey(); + struct CCcontract_info *cpDummy, C; + + cpDummy = CCinit(&C, EVAL_TOKENS); + + mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + if( (inputs = AddNormalinputs(mtx, myPubKey, txfee, 4)) == 0 ) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx: cannot find normal imputs for txfee" << std::endl); + return std::string(""); + } + + CScript scriptPubKey = GetScriptForDestination(dest); + mtx.vout.push_back(CTxOut(txfee, scriptPubKey)); + change = inputs - txfee; + if( change != 0 ) + mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG)); + + //make opret with amount: + return FinalizeCCTx(0, cpDummy, mtx, myPubKey, txfee, CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN << (uint8_t)'A' << amount)); +} + +// make sure vin0 is signed by ASSETCHAINS_OVERRIDE_PUBKEY33 +int32_t CheckVin0PubKey(const CTransaction &sourcetx) +{ + CTransaction vintx; + uint256 blockHash; + char destaddr[64], pkaddr[64]; + + if( !myGetTransaction(sourcetx.vin[0].prevout.hash, vintx, blockHash) ) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVin0PubKey() could not load vintx" << sourcetx.vin[0].prevout.hash.GetHex() << std::endl); + return(-1); + } + if( sourcetx.vin[0].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[0].prevout.n].scriptPubKey) != 0 ) + { + pubkey2addr(pkaddr, ASSETCHAINS_OVERRIDE_PUBKEY33); + if (strcmp(pkaddr, destaddr) == 0) { + return(0); + } + LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVin0PubKey() mismatched vin0[prevout.n=" << sourcetx.vin[0].prevout.n << "] -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl); + } + return -1; +} + +// ac_import=PUBKEY support: +// prepare a tx for creating import tx and quasi-burn tx +int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount) // find burnTx with hash from "other" daemon +{ + MerkleBranch newBranch; + CMutableTransaction tmpmtx; + CTransaction sourcetx; + + tmpmtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + if (!E_UNMARSHAL(ParseHex(rawsourcetx), ss >> sourcetx)) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: could not unmarshal source tx" << std::endl); + return(-1); + } + + if (sourcetx.vout.size() == 0) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: vout size is 0" << std::endl); + return -1; + } + + if (ivout < 0) { // "ivout < 0" means "find" + // try to find vout + CPubKey myPubkey = Mypubkey(); + ivout = 0; + // skip change: + if (sourcetx.vout[ivout].scriptPubKey == (CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)) + ivout++; + } + + if (ivout >= sourcetx.vout.size()) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: needed vout not found" << std::endl); + return -1; + } + + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl); + + scriptPubKey = sourcetx.vout[ivout].scriptPubKey; + + //mtx is template for import tx + mtx = sourcetx; + mtx.fOverwintered = tmpmtx.fOverwintered; + + //malleability fix for burn tx: + //mtx.nExpiryHeight = tmpmtx.nExpiryHeight; + mtx.nExpiryHeight = sourcetx.nExpiryHeight; + + mtx.nVersionGroupId = tmpmtx.nVersionGroupId; + mtx.nVersion = tmpmtx.nVersion; + mtx.vout.clear(); + mtx.vout.resize(1); + mtx.vout[0].nValue = burnAmount; + mtx.vout[0].scriptPubKey = scriptPubKey; + + // not sure we need this now as we create sourcetx ourselves: + if (sourcetx.GetHash() != sourcetxid) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl); + return(-1); + } + + // check ac_pubkey: + if (CheckVin0PubKey(sourcetx) < 0) { + return -1; + } + proof = std::make_pair(sourcetxid, newBranch); + return 0; +} + +// make import tx with burntx and dual daemon +std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector vouts) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()),burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; uint256 codaburntxid; std::vector dummyproof; + int32_t i,numvouts,n,m; std::string coin,error; struct CCcontract_info *cp, C; + cJSON *result,*tmp,*tmp1; unsigned char hash[SHA256_DIGEST_LENGTH+1]; + char out[SHA256_DIGEST_LENGTH*2+1],*retstr,*destaddr,*receiver; TxProof txProof; uint64_t amount; + + cp = CCinit(&C, EVAL_GATEWAYS); + if (txfee == 0) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + SHA256_CTX sha256; + SHA256_Init(&sha256); + SHA256_Update(&sha256, receipt.c_str(), receipt.size()); + SHA256_Final(hash, &sha256); + for(i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf(out + (i * 2), "%02x", hash[i]); + } + out[65]='\0'; + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: hash=" << out << std::endl); + codaburntxid.SetHex(out); + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receipt=" << receipt << " codaburntxid=" << codaburntxid.GetHex().data() << " amount=" << (double)amount / COIN << std::endl); + result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),""); + if (result==0) + { + if (retstr!=0) + { + CCerror=std::string("CodaRPC: ")+retstr; + free(retstr); + } + return(""); } else { - if ( !E_UNMARSHAL(rawtx, ss >> tx) ) - return(-1); - scriptPubKey = tx.vout[0].scriptPubKey; - mtx = tx; - mtx.fOverwintered = tmpmtx.fOverwintered; - mtx.nExpiryHeight = tmpmtx.nExpiryHeight; - mtx.nVersionGroupId = tmpmtx.nVersionGroupId; - mtx.nVersion = tmpmtx.nVersion; - mtx.vout.clear(); - mtx.vout.resize(1); - mtx.vout[0].nValue = burnAmount; - mtx.vout[0].scriptPubKey = scriptPubKey; - if ( tx.GetHash() != txid ) - return(-1); - if ( source == "PUBKEY" ) + if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))!=0 && (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))!=0 && + (receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))!=0 && (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))!=0) { - // make sure vin0 is signed by ASSETCHAINS_OVERRIDE_PUBKEY33 - if ( myGetTransaction(tx.vin[0].prevout.hash,vintx,blockHash) == 0 ) - return(-1); - if ( tx.vin[0].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr,vintx.vout[tx.vin[0].prevout.n].scriptPubKey) != 0 ) + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeCodaImportTx: receiver=" << receiver << " destaddr=" << destaddr << " amount=" << amount << std::endl); + if (strcmp(receiver,CODA_BURN_ADDRESS)!=0) { - pubkey2addr(pkaddr,ASSETCHAINS_OVERRIDE_PUBKEY33); - if ( strcmp(pkaddr,destaddr) == 0 ) - { - proof = std::make_pair(txid,newBranch); - return(0); - } - fprintf(stderr,"mismatched vin0[%d] -> %s vs %s\n",tx.vin[0].prevout.n,destaddr,pkaddr); + CCerror="MakeCodaImportTx: invalid burn address, coins do not go to predefined burn address - "; + CCerror+=CODA_BURN_ADDRESS; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); } + CTxDestination dest = DecodeDestination(destaddr); + CScript scriptPubKey = GetScriptForDestination(dest); + if (vouts[0]!=CTxOut(amount*COIN,scriptPubKey)) + { + CCerror="MakeCodaImportTx: invalid destination address, burnTx memo!=importTx destination"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); + } + if (amount*COIN!=vouts[0].nValue) + { + CCerror="MakeCodaImportTx: invalid amount, burnTx amount!=importTx amount"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); + } + burntx.vin.push_back(CTxIn(codaburntxid,0,CScript())); + burntx.vout.push_back(MakeBurnOutput(amount*COIN,0xffffffff,"CODA",vouts,dummyproof,srcaddr,receipt)); + return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof,burntx,vouts))); } - else if ( source == ASSETCHAINS_SELFIMPORT ) + else { - // source is external coin is the assetchains symbol in the burnTx OP_RETURN - // burnAmount, rawtx and rawproof should be enough for gatewaysdeposit equivalent + CCerror="MakeCodaImportTx: invalid Coda burn tx"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); } + } - return(-1); + CCerror="MakeCodaImportTx: error fetching Coda tx"; + LOGSTREAM("importcoin", CCLOG_INFO, stream << CCerror << std::endl); + free(result); + return(""); } // use proof from the above functions to validate the import @@ -100,31 +389,202 @@ int32_t CheckBEAMimport(TxProof proof,std::vector rawproof,CTransaction return(-1); } -int32_t CheckCODAimport(TxProof proof,std::vector rawproof,CTransaction burnTx,std::vector payouts) +int32_t CheckCODAimport(CTransaction importTx,CTransaction burnTx,std::vector payouts,std::string srcaddr,std::string receipt) { + cJSON *result,*tmp,*tmp1; char *retstr,out[SHA256_DIGEST_LENGTH*2+1]; unsigned char hash[SHA256_DIGEST_LENGTH+1]; int i,n,m; + SHA256_CTX sha256; uint256 codaburntxid; char *destaddr,*receiver; uint64_t amount; + // check with dual-CODA daemon via ASSETCHAINS_CODAPORT for validity of burnTx - return(-1); + SHA256_Init(&sha256); + SHA256_Update(&sha256, receipt.c_str(), receipt.size()); + SHA256_Final(hash, &sha256); + for(i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf(out + (i * 2), "%02x", hash[i]); + } + out[65]='\0'; + codaburntxid.SetHex(out); + result=CodaRPC(&retstr,"prove-payment","-address",srcaddr.c_str(),"-receipt-chain-hash",receipt.c_str(),""); + if (result==0) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "CodaRPC error: " << retstr << std::endl); + free(retstr); + return (-1); + } + else + { + if ((tmp=jobj(jitem(jarray(&n,result,(char *)"payments"),0),(char *)"payload"))==0 || (destaddr=jstr(jobj(tmp,(char *)"common"),(char *)"memo"))==0 || + (receiver=jstr(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"receiver"))==0 || (amount=j64bits(jitem(jarray(&m,tmp,(char *)"body"),1),(char *)"amount"))==0) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid Coda burn tx" << jprint(result,1) << std::endl); + free(result); + return (-1); + } + CTxDestination dest = DecodeDestination(destaddr); + CScript scriptPubKey = GetScriptForDestination(dest); + if (payouts[0]!=CTxOut(amount*COIN,scriptPubKey)); + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Destination address in burn tx does not match destination in import tx" << std::endl); + free(result); + return (-1); + } + if (strcmp(receiver,CODA_BURN_ADDRESS)!=0) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid burn address " << jstr(tmp1,(char *)"receiver") << std::endl); + free(result); + return (-1); + } + if (amount*COIN!=payouts[0].nValue) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Burn amount and import amount not matching, " << j64bits(tmp,(char *)"amount") << " - " << payouts[0].nValue/COIN << std::endl); + free(result); + return (-1); + } + if (burnTx.vin[0].prevout.hash!=codaburntxid || importTx.vin[0].prevout.hash!=burnTx.GetHash()) + { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "Invalid import/burn tx vin" << std::endl); + free(result); + return (-1); + } + free(result); + } + return(0); } -int32_t CheckGATEWAYimport(TxProof proof,std::vector rawproof,CTransaction burnTx,std::vector payouts) +int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction burnTx,std::string refcoin,std::vector proof, + uint256 bindtxid,std::vector publishers,std::vector txids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub) { + // CTransaction oracletx,regtx; CPubKey regpk; + // uint256 proofroot,txid,tmporacletxid,merkleroot,mhash,hashBlock; int32_t i,m,n=0,numvouts; + // std::string name,desc,format; std::vector vouts; + // std::vector pubkeys; std::vectortxids; + // char markeraddr[64]; int64_t datafee; + // std::vector > unspentOutputs; // ASSETCHAINS_SELFIMPORT is coin // check for valid burn from external coin blockchain and if valid return(0); - return(-1); + // if (GetTransaction(oracletxid, oracletx, hashBlock, false) == 0 || (numvouts = oracletx.vout.size()) <= 0) + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl); + // return(-1); + // } + // if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C') + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl); + // return(-1); + // } + // if (name!=refcoin || format!="Ihh") + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl); + // return(-1); + // } + // CCtxidaddr(markeraddr,oracletxid); + // SetCCunspents(unspentOutputs,markeraddr,true); + // for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + // { + // txid = it->first.txhash; + // if ( GetTransaction(txid,regtx,hashBlock,false) != 0 && regtx.vout.size() > 0 + // && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid ) + // { + // pubkeys.push_back(regpk); + // n++; + // } + // } + // merkleroot = zeroid; + // for (i = m = 0; i < n; i++) + // { + // if ((mhash = CCOraclesReverseScan("importcoind-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid) + // { + // if (merkleroot == zeroid) + // merkleroot = mhash, m = 1; + // else if (mhash == merkleroot) + // m ++; + // txids.push_back(txid); + // } + // } + // if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl ); + // return(-1); + // } + // proofroot = BitcoinGetProofMerkleRoot(proof, txids); + // if (proofroot != merkleroot) + // { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport mismatched proof merkleroot=" << proofroot.GetHex() << " and oracles merkleroot=" << merkleroot.GetHex() << std::endl); + // return(-1); + // } + // // check the burntxid is in the proof: + // if (std::find(txids.begin(), txids.end(), burnTx.GetHash()) == txids.end()) { + // LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid proof for this burntxid=" << burnTx.GetHash().GetHex() << std::endl); + // return(-1); + // } + return(0); } int32_t CheckPUBKEYimport(TxProof proof,std::vector rawproof,CTransaction burnTx,std::vector payouts) { // if burnTx has ASSETCHAINS_PUBKEY vin, it is valid return(0); - fprintf(stderr,"proof txid.%s\n",proof.first.GetHex().c_str()); + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "proof txid=" << proof.first.GetHex() << std::endl); + + uint256 sourcetxid = proof.first, hashBlock; + CTransaction sourcetx; + + if (!myGetTransaction(sourcetxid, sourcetx, hashBlock)) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "could not load source txid=" << sourcetxid.GetHex() << std::endl); + return -1; + } + + if (sourcetx.vout.size() == 0) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "no vouts in source txid=" << sourcetxid.GetHex() << std::endl); + return -1; + } + + // might be malleable: + if (burnTx.nExpiryHeight != sourcetx.nExpiryHeight) { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "burntx nExpiryHeight incorrect for source txid=" << sourcetxid.GetHex() << std::endl); + return -1; + } + + //ac_pubkey check: + if (CheckVin0PubKey(sourcetx) < 0) { + return -1; + } + + // get source tx opret: + std::vector vopret; + uint8_t evalCode, funcId; + int64_t amount; + + GetOpReturnData(sourcetx.vout.back().scriptPubKey, vopret); + if (vopret.size() == 0 || !E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) || evalCode != EVAL_IMPORTCOIN || funcId != 'A') { + LOGSTREAM("importcoin", CCLOG_INFO, stream << "no or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl); + return -1; + } + + LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "importTx amount=" << payouts[0].nValue << " burnTx amount=" << burnTx.vout[0].nValue << " opret amount=" << amount << " source txid=" << sourcetxid.GetHex() << std::endl); + + // amount malleability check with the opret from the source tx: + if (payouts[0].nValue != amount) { // assume that burntx amount is checked in the common code in Eval::ImportCoin() + LOGSTREAM("importcoin", CCLOG_INFO, stream << "importTx amount != amount in the opret of source txid=" << sourcetxid.GetHex() << std::endl); + return -1; + } + return(0); - return(-1); } +/* + * CC Eval method for import coin. + * + * This method should control every parameter of the ImportCoin transaction, since it has no signature + * to protect it from malleability. + + ##### 0xffffffff is a special CCid for single chain/dual daemon imports + */ bool Eval::ImportCoin(const std::vector params,const CTransaction &importTx,unsigned int nIn) { - TxProof proof; CTransaction burnTx; std::vector payouts; uint64_t txfee = 10000; - uint32_t targetCcid; std::string targetSymbol; uint256 payoutsHash; std::vector rawproof; + TxProof proof; CTransaction burnTx; std::vector payouts; uint64_t txfee = 10000; int32_t height,burnvout; std::vector publishers; + uint32_t targetCcid; std::string targetSymbol,srcaddr,destaddr,receipt,rawburntx; uint256 payoutsHash,bindtxid; std::vector rawproof; + std::vector txids; CPubKey destpub; + if ( importTx.vout.size() < 2 ) return Invalid("too-few-vouts"); // params @@ -132,10 +592,10 @@ bool Eval::ImportCoin(const std::vector params,const CTransaction &impo return Invalid("invalid-params"); // Control all aspects of this transaction // It should not be at all malleable - if (MakeImportCoinTransaction(proof, burnTx, payouts).GetHash() != importTx.GetHash()) + if (MakeImportCoinTransaction(proof, burnTx, payouts, importTx.nExpiryHeight).GetHash() != importTx.GetHash()) // ExistsImportTombstone prevents from duplication return Invalid("non-canonical"); // burn params - if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash,rawproof)) + if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash, rawproof)) return Invalid("invalid-burn-tx"); // check burn amount { @@ -175,7 +635,7 @@ bool Eval::ImportCoin(const std::vector params,const CTransaction &impo { if ( ASSETCHAINS_CODAPORT == 0 ) return Invalid("CODA-import-without-port"); - else if ( CheckCODAimport(proof,rawproof,burnTx,payouts) < 0 ) + else if ( UnmarshalBurnTx(burnTx,srcaddr,receipt)==0 || CheckCODAimport(importTx,burnTx,payouts,srcaddr,receipt) < 0 ) return Invalid("CODA-import-failure"); } else if ( targetSymbol == "PUBKEY" ) @@ -189,11 +649,9 @@ bool Eval::ImportCoin(const std::vector params,const CTransaction &impo { if ( targetSymbol != ASSETCHAINS_SELFIMPORT ) return Invalid("invalid-gateway-import-coin"); - else if ( CheckGATEWAYimport(proof,rawproof,burnTx,payouts) < 0 ) + else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,height,burnvout,rawburntx,destpub)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,height,burnvout,rawburntx,destpub) < 0 ) return Invalid("GATEWAY-import-failure"); } } return Valid(); } - - diff --git a/src/cc/importgateway.cpp b/src/cc/importgateway.cpp new file mode 100644 index 000000000..f7bd9b669 --- /dev/null +++ b/src/cc/importgateway.cpp @@ -0,0 +1,1292 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include "CCImportGateway.h" +#include "key_io.h" +#include "../importcoin.h" + +// start of consensus code + +#define KMD_PUBTYPE 60 +#define KMD_P2SHTYPE 85 +#define KMD_WIFTYPE 188 +#define KMD_TADDR 0 +#define CC_MARKER_VALUE 10000 + +CScript EncodeImportGatewayBindOpRet(uint8_t funcid,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector importgatewaypubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2,uint8_t wiftype) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << oracletxid << M << N << importgatewaypubkeys << taddr << prefix << prefix2 << wiftype); + return(opret); +} + +uint8_t DecodeImportGatewayBindOpRet(char *burnaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype) +{ + std::vector vopret; uint8_t *script,e,f; std::vector pubkeys; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + burnaddr[0] = 0; + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> oracletxid; ss >> M; ss >> N; ss >> importgatewaypubkeys; ss >> taddr; ss >> prefix; ss >> prefix2; ss >> wiftype) != 0 ) + { + if ( prefix == KMD_PUBTYPE && prefix2 == KMD_P2SHTYPE ) + { + if ( N > 1 ) + { + strcpy(burnaddr,CBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys))).ToString().c_str()); + LOGSTREAM("importgateway", CCLOG_DEBUG1, stream << "f." << f << " M." << (int)M << " of N." << (int)N << " size." << (int32_t)importgatewaypubkeys.size() << " -> " << burnaddr << std::endl); + } else Getscriptaddress(burnaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG); + } + else + { + if ( N > 1 ) strcpy(burnaddr,CCustomBitcoinAddress(CScriptID(GetScriptForMultisig(M,importgatewaypubkeys)),taddr,prefix,prefix2).ToString().c_str()); + else GetCustomscriptaddress(burnaddr,CScript() << ParseHex(HexStr(importgatewaypubkeys[0])) << OP_CHECKSIG,taddr,prefix,prefix2); + } + return(f); + } else LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "error decoding bind opret" << std::endl); + return(0); +} + +CScript EncodeImportGatewayDepositOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,std::vector publishers,std::vectortxids,int32_t height,uint256 burntxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << refcoin << bindtxid << publishers << txids << height << burntxid << claimvout << deposithex << proof << destpub << amount); + return(opret); +} + +uint8_t DecodeImportGatewayDepositOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,std::vector&publishers,std::vector&txids,int32_t &height,uint256 &burntxid, int32_t &claimvout,std::string &deposithex,std::vector &proof,CPubKey &destpub,int64_t &amount) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> refcoin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> burntxid; ss >> claimvout; ss >> deposithex; ss >> proof; ss >> destpub; ss >> amount) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayWithdrawOpRet(uint8_t funcid,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << bindtxid << refcoin << withdrawpub << amount); + return(opret); +} + +uint8_t DecodeImportGatewayWithdrawOpRet(const CScript &scriptPubKey,uint256 &bindtxid,std::string &refcoin,CPubKey &withdrawpub,int64_t &amount) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bindtxid; ss >> refcoin; ss >> withdrawpub; ss >> amount) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayPartialOpRet(uint8_t funcid, uint256 withdrawtxid,std::string refcoin,uint8_t K, CPubKey signerpk,std::string hex) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << signerpk << hex); + return(opret); +} + +uint8_t DecodeImportGatewayPartialOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,CPubKey &signerpk,std::string &hex) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> signerpk; ss >> hex) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayCompleteSigningOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint8_t K,std::string hex) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << K << hex); + return(opret); +} + +uint8_t DecodeImportGatewayCompleteSigningOpRet(const CScript &scriptPubKey,uint256 &withdrawtxid,std::string &refcoin,uint8_t &K,std::string &hex) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> K; ss >> hex) != 0 ) + { + return(f); + } + return(0); +} + +CScript EncodeImportGatewayMarkDoneOpRet(uint8_t funcid,uint256 withdrawtxid,std::string refcoin,uint256 completetxid) +{ + CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY; + + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << withdrawtxid << refcoin << completetxid); + return(opret); +} + +uint8_t DecodeImportGatewayMarkDoneOpRet(const CScript &scriptPubKey, uint256 &withdrawtxid, std::string &refcoin, uint256 &completetxid) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> withdrawtxid; ss >> refcoin; ss >> completetxid;) != 0 ) + { + return(f); + } + return(0); +} + +uint8_t DecodeImportGatewayOpRet(const CScript &scriptPubKey) +{ + std::vector vopret; uint8_t *script,e,f; + + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && script[0] == EVAL_IMPORTGATEWAY) + { + f=script[1]; + if (f == 'B' || f == 'D' || f == 'C' || f == 'W' || f == 'P' || f == 'S' || f == 'M') + return(f); + } + return(0); +} + +int64_t IsImportGatewayvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +{ + char destaddr[64]; + + if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) + { + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + return(tx.vout[v].nValue); + } + return(0); +} + +int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vectorproof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2) +{ + std::vector txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; + char destaddr[64],destpubaddr[64],claimaddr[64]; int32_t i,numvouts; int64_t nValue = 0; + + if ( myGetTransaction(oracletxid,tx,hashBlock) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify cant find oracletxid " << oracletxid.GetHex() << std::endl); + return(0); + } + if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify mismatched oracle name " << name << " != " << refcoin << std::endl); + return(0); + } + proofroot = BitcoinGetProofMerkleRoot(proof,txids); + if ( proofroot != merkleroot ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify mismatched merkleroot " << proofroot.GetHex() << " != " << merkleroot.GetHex() << std::endl); + return(0); + } + if (std::find(txids.begin(), txids.end(), burntxid) == txids.end()) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify invalid proof for this burntxid" << std::endl); + return 0; + } + if ( DecodeHexTx(tx,deposithex) != 0 ) + { + GetCustomscriptaddress(claimaddr,tx.vout[claimvout].scriptPubKey,taddr,prefix,prefix2); + GetCustomscriptaddress(destpubaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + if ( strcmp(claimaddr,destpubaddr) == 0 ) + { + for (i=0; i publishers; std::vectortxids; uint256 bindtxid,burntxid; std::vector proof; CPubKey claimpubkey; + if ( (numvouts= tx.vout.size()) > 0 ) + { + if ( DecodeImportGatewayDepositOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,publishers,txids,height,burntxid,claimvout,deposithex,proof,claimpubkey,amount) == 'D' && claimpubkey == mypk ) + { + return(amount); + } + } + return(0); +} + +int32_t ImportGatewayBindExists(struct CCcontract_info *cp,CPubKey importgatewaypk,std::string refcoin) +{ + char markeraddr[64],burnaddr[64]; std::string coin; int32_t numvouts; int64_t totalsupply; uint256 tokenid,oracletxid,hashBlock; + uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; CTransaction tx; + std::vector > addressIndex; + + _GetCCaddress(markeraddr,EVAL_IMPORTGATEWAY,importgatewaypk); + SetCCtxids(addressIndex,markeraddr,true); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + if ( myGetTransaction(it->first.txhash,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)=='B' ) + { + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B' ) + { + if ( coin == refcoin ) + { + LOGSTREAM("importgateway",CCLOG_INFO, stream << "trying to bind an existing import for coin" << std::endl); + return(1); + } + } + } + } + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + + if ((numvouts=txmempool.vout.size()) > 0 && DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)=='B') + if (DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) == 'B') + return(1); + } + + return(0); +} + +bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) +{ + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks,height,claimvout; bool retval; uint8_t funcid,hash[32],K,M,N,taddr,prefix,prefix2,wiftype; + char str[65],destaddr[65],burnaddr[65],importgatewayaddr[65],validationError[512]; + std::vector txids; std::vector pubkeys,publishers,tmppublishers; std::vector proof; int64_t amount,tmpamount; + uint256 hashblock,txid,bindtxid,deposittxid,withdrawtxid,completetxid,tmptokenid,oracletxid,bindtokenid,burntxid,tmptxid,merkleroot,mhash; CTransaction bindtx,tmptx; + std::string refcoin,tmprefcoin,hex,name,description,format; CPubKey pubkey,tmppubkey,importgatewaypk; + + return (true); + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + preventCCvins = preventCCvouts = -1; + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else + { + //LogPrint("importgateway-1","check amounts\n"); + // if ( ImportGatewayExactAmounts(cp,eval,tx,1,10000) == false ) + // { + // return eval->Invalid("invalid inputs vs. outputs!"); + // } + // else + // { + importgatewaypk = GetUnspendable(cp,0); + GetCCaddress(cp, importgatewayaddr, importgatewaypk); + if ( (funcid = DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey)) != 0) + { + switch ( funcid ) + { + case 'B': + //vin.0: normal input + //vout.0: CC vout marker + //vout.n-1: opreturn - 'B' coin oracletxid M N pubkeys taddr prefix prefix2 wiftype + return eval->Invalid("unexpected ImportGatewayValidate for gatewaysbind!"); + break; + case 'W': + //vin.0: normal input + //vin.1: CC input of tokens + //vout.0: CC vout marker to gateways CC address + //vout.1: CC vout of gateways tokens back to gateways tokens CC address + //vout.2: CC vout change of tokens back to owners pubkey (if any) + //vout.n-1: opreturn - 'W' bindtxid refcoin withdrawpub amount + return eval->Invalid("unexpected ImportGatewayValidate for gatewaysWithdraw!"); + break; + case 'P': + //vin.0: normal input + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'P' withdrawtxid refcoin number_of_signs mypk hex + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,pubkey,hex)!='P') + return eval->Invalid("invalid gatewaysPartialSign OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,importgatewayaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayspartialsign!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayspartialsign or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!"); + else if (K>M) + return eval->Invalid("invalid number of signs!"); + break; + case 'S': + //vin.0: normal input + //vin.1: CC input of marker from previous tx (withdraw or partialsing) + //vout.0: CC vout marker to gateways CC address + //vout.n-1: opreturn - 'S' withdrawtxid refcoin hex + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysWithdraw!"); + else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 ) + return eval->Invalid("vin.1 is CC for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0) + return eval->Invalid("invalid marker vout for gatewaysWithdraw!"); + else if ( ConstrainVout(tmptx.vout[1],1,importgatewayaddr,amount)==0) + return eval->Invalid("invalid tokens to gateways vout for gatewaysWithdraw!"); + else if (tmptx.vout[1].nValue!=amount) + return eval->Invalid("amount in opret not matching tx tokens amount!"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if (IsCCInput(tx.vin[0].scriptSig) != 0) + return eval->Invalid("vin.0 is normal for gatewayscompletesigning!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewayscompletesigning or invalid marker amount!"); + else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 ) + return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!"); + else if (KInvalid("invalid number of signs!"); + break; + case 'M': + //vin.0: normal input + //vin.1: CC input of gatewayscompletesigning tx marker to gateways CC address + //vout.0: opreturn - 'M' withdrawtxid refcoin completetxid + if ((numvouts=tx.vout.size()) > 0 && DecodeImportGatewayMarkDoneOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,refcoin,completetxid)!='M') + return eval->Invalid("invalid gatewaysmarkdone OP_RETURN data!"); + else if (myGetTransaction(completetxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewayscompletesigning txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayCompleteSigningOpRet(tmptx.vout[numvouts-1].scriptPubKey,withdrawtxid,tmprefcoin,K,hex)!='S') + return eval->Invalid("invalid gatewayscompletesigning OP_RETURN data!"); + else if (komodo_txnotarizedconfirmed(completetxid) == false) + return eval->Invalid("gatewayscompletesigning tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(withdrawtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid withdraw txid!"); + else if ((numvouts=tmptx.vout.size()) > 0 && DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,tmprefcoin,pubkey,amount)!='W') + return eval->Invalid("invalid gatewayswithdraw OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(withdrawtxid) == false) + return eval->Invalid("gatewayswithdraw tx is not yet confirmed(notarised)!"); + else if (myGetTransaction(bindtxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid gatewaysbind txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,tmprefcoin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B') + return eval->Invalid("invalid gatewaysbind OP_RETURN data!"); + else if (tmprefcoin!=refcoin) + return eval->Invalid("refcoin different than in bind tx"); + else if (komodo_txnotarizedconfirmed(bindtxid) == false) + return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for gatewaysmarkdone!"); + else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.1 is CC marker for gatewaysmarkdone or invalid marker amount!"); + else if (KInvalid("invalid number of signs!"); + break; + } + } + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGateway tx validated" << std::endl); + else fprintf(stderr,"ImportGateway tx invalid\n"); + return(retval); + // } + } +} +// end of consensus code + +// helper functions for rpc calls in rpcwallet.cpp + +std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction oracletx; uint8_t taddr,prefix,prefix2,wiftype; CPubKey mypk,importgatewaypk; CScript opret; uint256 hashBlock; + struct CCcontract_info *cp,*cpTokens,C,CTokens; std::string name,description,format; int32_t i,numvouts; + char destaddr[64],coinaddr[64],myTokenCCaddr[64],str[65],*fstr; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + cpTokens = CCinit(&CTokens,EVAL_TOKENS); + if (coin=="KMD") + { + prefix = KMD_PUBTYPE; + prefix2 = KMD_P2SHTYPE; + wiftype = KMD_WIFTYPE; + taddr = KMD_TADDR; + } + else + { + prefix = p1; + prefix2 = p2; + wiftype = p3; + taddr = p4; + LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "set prefix " << prefix << ", prefix2 " << prefix2 << ", wiftype " << wiftype << ", taddr " << taddr << " for " << coin << std::endl); + } + if ( N == 0 || N > 15 || M > N ) + { + CCerror = strprintf("illegal M.%d or N.%d",M,N); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( pubkeys.size() != N ) + { + CCerror = strprintf("M.%d N.%d but pubkeys[%d]",M,N,(int32_t)pubkeys.size()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + for (i=0; i 0 ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CC_MARKER_VALUE,importgatewaypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayBindOpRet('B',coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype))); + } + CCerror = strprintf("cant find enough inputs"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t claimvout,std::string rawburntx,std::vectorproof,CPubKey destpub) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()), burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction bindtx; CPubKey mypk; uint256 oracletxid,merkleroot,mhash,hashBlock,txid; std::vector vouts; + int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C; + std::vector pubkeys,publishers; std::vector txids; char str[128],burnaddr[64]; int64_t amount; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + if (!E_UNMARSHAL(ParseHex(rawburntx), ss >> burntx)) + { + return std::string(""); + } + amount=burntx.vout[0].nValue; + LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "ImportGatewayDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl); + if ( GetTransaction(bindtxid,bindtx,hashBlock,false) == 0 || (numvouts= bindtx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) + { + CCerror = strprintf("invalid coin - bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(bindtxid)==false) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + n = (int32_t)pubkeys.size(); + merkleroot = zeroid; + for (i=m=0; i leaftxids; + BitcoinGetProofMerkleRoot(proof, leaftxids); + MerkleBranch newBranch(0, leaftxids); + TxProof txProof = std::make_pair(burntxid, newBranch); + return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof, burntx, vouts))); +} + +std::string ImportGatewayWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction tx; CPubKey mypk,importgatewaypk,signerpk; uint256 txid,hashBlock,oracletxid,tmptokenid,tmpbindtxid,withdrawtxid; int32_t vout,numvouts; + int64_t nValue,inputs,CCchange=0,tmpamount; uint8_t funcid,K,M,N,taddr,prefix,prefix2,wiftype; std::string coin,hex; + std::vector msigpubkeys; char burnaddr[64],str[65],coinaddr[64]; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp, 0); + + if( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || coin != refcoin ) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(bindtxid)==false) + { + CCerror = strprintf("gatewaysbind tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + K=0; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P')) + { + if (funcid=='W' && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + else if (funcid=='P' && DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)=='P' && + GetTransaction(withdrawtxid,tx,hashBlock,false)!=0 && (numvouts=tx.vout.size())>0 && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,tmpamount)=='W' + && refcoin==coin && tmpbindtxid==bindtxid) + { + CCerror = strprintf("unable to create withdraw, another withdraw pending"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + } + if( AddNormalinputs(mtx, mypk, txfee+CC_MARKER_VALUE+amount, 64) > 0 ) + { + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,amount,importgatewaypk)); + return(FinalizeCCTx(0, cp, mtx, mypk, txfee,EncodeImportGatewayWithdrawOpRet('W',bindtxid,refcoin,withdrawpub,amount))); + } + CCerror = strprintf("cant find enough normal inputs"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,withdrawpub,signerpk,importgatewaypk; struct CCcontract_info *cp,C; CTransaction tx,tmptx; + std::vector > unspentOutputs; char funcid,str[65],burnaddr[64]; + int32_t numvouts; uint256 withdrawtxid,hashBlock,bindtxid,tokenid,oracletxid; std::string coin,tmphex; int64_t amount; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + if (GetTransaction(lasttxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + { + CCerror = strprintf("can't find last tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (funcid=='W') + { + withdrawtxid=lasttxid; + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + else if (funcid=='P') + { + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + { + CCerror = strprintf("cannot decode partialsign tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tmptx,hashBlock,false)==0 || (numvouts= tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find withdraw tx %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin) + { + CCerror = strprintf("invalid withdraw tx %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(tx.GetHash(),0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayPartialOpRet('P',withdrawtxid,refcoin,K+1,mypk,hex))); + } + CCerror = strprintf("error adding funds for partialsign"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,importgatewaypk,signerpk,withdrawpub; struct CCcontract_info *cp,C; char funcid,str[65],burnaddr[64]; int64_t amount; + std::string coin,tmphex; CTransaction tx,tmptx; uint256 withdrawtxid,hashBlock,tokenid,bindtxid,oracletxid; int32_t numvouts; + uint8_t K=0,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + if ( txfee == 0 ) + txfee = 10000; + if (GetTransaction(lasttxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0 + || (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))==0 || (funcid!='W' && funcid!='P')) + { + CCerror = strprintf("invalid last txid %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (funcid=='W') + { + withdrawtxid=lasttxid; + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + else if (funcid=='P') + { + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,tmphex)!='P' || refcoin!=coin) + { + CCerror = strprintf("cannot decode partialsign tx opret %s",uint256_str(str,lasttxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())==0) + { + CCerror = strprintf("invalid withdraw txid %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tmptx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (komodo_txnotarizedconfirmed(withdrawtxid)==false) + { + CCerror = strprintf("gatewayswithdraw tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tmptx,hashBlock,false)==0 || (numvouts=tmptx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tmptx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(lasttxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(EVAL_IMPORTGATEWAY,CC_MARKER_VALUE,importgatewaypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayCompleteSigningOpRet('S',withdrawtxid,refcoin,K+1,hex))); + } + CCerror = strprintf("error adding funds for completesigning"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +std::string ImportGatewayMarkDone(uint64_t txfee,uint256 completetxid,std::string refcoin) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk; struct CCcontract_info *cp,C; char str[65],burnaddr[64]; CTransaction tx; int32_t numvouts; + uint256 withdrawtxid,bindtxid,oracletxid,tokenid,hashBlock; std::string coin,hex; + uint8_t K,M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; int64_t amount; CPubKey withdrawpub; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + if ( txfee == 0 ) + txfee = 10000; + if (GetTransaction(completetxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())<=0) + { + CCerror = strprintf("invalid completesigning txid %s",uint256_str(str,completetxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex)!='S' || refcoin!=coin) + { + CCerror = strprintf("cannot decode completesigning tx opret %s",uint256_str(str,completetxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (komodo_txnotarizedconfirmed(completetxid)==false) + { + CCerror = strprintf("gatewayscompletesigning tx not yet confirmed/notarized"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(withdrawtxid,tx,hashBlock,false)==0 || (numvouts= tx.vout.size())==0) + { + CCerror = strprintf("invalid withdraw txid %s",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount)!='W' || refcoin!=coin) + { + CCerror = strprintf("cannot decode withdraw tx opret %s\n",uint256_str(str,withdrawtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (GetTransaction(bindtxid,tx,hashBlock,false)==0 || (numvouts=tx.vout.size())<=0) + { + CCerror = strprintf("can't find bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + else if (DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' + || refcoin!=coin) + { + CCerror = strprintf("invalid bind tx %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (AddNormalinputs(mtx,mypk,txfee,3)!=0) + { + mtx.vin.push_back(CTxIn(completetxid,0,CScript())); + mtx.vout.push_back(CTxOut(CC_MARKER_VALUE,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeImportGatewayMarkDoneOpRet('M',withdrawtxid,refcoin,completetxid))); + } + CCerror = strprintf("error adding funds for markdone"); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); +} + +UniValue ImportGatewayPendingDeposits(uint256 bindtxid,std::string refcoin) +{ + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex,pub; + CPubKey mypk,importgatewaypk,destpub; std::vector pubkeys,publishers; std::vector txids; + uint256 tmpbindtxid,hashBlock,txid,oracletxid,burntxid; uint8_t M,N,taddr,prefix,prefix2,wiftype; + char burnaddr[65],coinaddr[65],str[65],destaddr[65],txidaddr[65]; std::vector proof; + int32_t numvouts,vout,claimvout,height; int64_t nValue,amount; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,mypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size())>0 && + DecodeImportGatewayDepositOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,publishers,txids,height,burntxid,claimvout,hex,proof,destpub,amount) == 'D' + && tmpbindtxid==bindtxid && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("burntxid",uint256_str(str,burntxid))); + obj.push_back(Pair("deposittxid",uint256_str(str,txid))); + CCtxidaddr(txidaddr,txid); + obj.push_back(Pair("deposittxidaddr",txidaddr)); + _GetCCaddress(destaddr,EVAL_TOKENS,destpub); + obj.push_back(Pair("depositaddr",burnaddr)); + obj.push_back(Pair("tokens_destination_address",destaddr)); + pub=HexStr(destpub); + obj.push_back(Pair("claim_pubkey",pub)); + obj.push_back(Pair("amount",(double)amount/COIN)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + pending.push_back(obj); + } + } + result.push_back(Pair("coin",refcoin)); + result.push_back(Pair("pending",pending)); + return(result); +} + +UniValue ImportGatewayPendingWithdraws(uint256 bindtxid,std::string refcoin) +{ + UniValue result(UniValue::VOBJ),pending(UniValue::VARR); CTransaction tx; std::string coin,hex; CPubKey mypk,importgatewaypk,withdrawpub,signerpk; + std::vector msigpubkeys; uint256 hashBlock,txid,tmpbindtxid,tmptokenid,oracletxid,withdrawtxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char funcid,burnaddr[65],coinaddr[65],destaddr[65],str[65],withaddr[65],numstr[32],signeraddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t amount,nValue; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin ) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + n = msigpubkeys.size(); + queueflag = 0; + for (i=0; i >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + K=0; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + (funcid=DecodeImportGatewayOpRet(tx.vout[numvouts-1].scriptPubKey))!=0 && (funcid=='W' || funcid=='P') && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + if (funcid=='W') + { + if (DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,amount)==0 || refcoin!=coin || tmpbindtxid!=bindtxid) continue; + } + else if (funcid=='P') + { + if (DecodeImportGatewayPartialOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,signerpk,hex)!='P' || GetTransaction(withdrawtxid,tx,hashBlock,false)==0 + || (numvouts=tx.vout.size())<=0 || DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,tmpbindtxid,coin,withdrawpub,amount)!='W' + || refcoin!=coin || tmpbindtxid!=bindtxid) + continue; + } + Getscriptaddress(destaddr,tx.vout[1].scriptPubKey); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + if ( strcmp(destaddr,coinaddr) == 0 ) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("withdrawtxid",uint256_str(str,tx.GetHash()))); + CCCustomtxidaddr(txidaddr,tx.GetHash(),taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); + obj.push_back(Pair("withdrawaddr",withaddr)); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(tx.GetHash()))); + if ( queueflag != 0 ) + { + obj.push_back(Pair("depositaddr",burnaddr)); + GetCustomscriptaddress(signeraddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG,taddr,prefix,prefix2); + obj.push_back(Pair("signeraddr",signeraddr)); + } + if (N>1) + { + obj.push_back(Pair("number_of_signs",K)); + obj.push_back(Pair("last_txid",uint256_str(str,txid))); + if (K>0) obj.push_back(Pair("hex",hex)); + } + pending.push_back(obj); + } + } + } + result.push_back(Pair("coin",refcoin)); + result.push_back(Pair("pending",pending)); + result.push_back(Pair("queueflag",queueflag)); + return(result); +} + +UniValue ImportGatewayProcessedWithdraws(uint256 bindtxid,std::string refcoin) +{ + UniValue result(UniValue::VOBJ),processed(UniValue::VARR); CTransaction tx; std::string coin,hex; + CPubKey mypk,importgatewaypk,withdrawpub; std::vector msigpubkeys; + uint256 withdrawtxid,hashBlock,txid,tmptokenid,oracletxid; uint8_t K,M,N,taddr,prefix,prefix2,wiftype; + char burnaddr[65],coinaddr[65],str[65],numstr[32],withaddr[65],txidaddr[65]; + int32_t i,n,numvouts,vout,queueflag; int64_t nValue,amount; struct CCcontract_info *cp,C; + std::vector > unspentOutputs; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + mypk = pubkey2pk(Mypubkey()); + importgatewaypk = GetUnspendable(cp,0); + _GetCCaddress(coinaddr,EVAL_IMPORTGATEWAY,importgatewaypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B' || refcoin != coin) + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + n = msigpubkeys.size(); + queueflag = 0; + for (i=0; i >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second.satoshis; + if ( vout == 0 && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 && + DecodeImportGatewayCompleteSigningOpRet(tx.vout[numvouts-1].scriptPubKey,withdrawtxid,coin,K,hex) == 'S' && refcoin == coin && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0) + { + if (GetTransaction(withdrawtxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size())>0 + && DecodeImportGatewayWithdrawOpRet(tx.vout[numvouts-1].scriptPubKey,bindtxid,coin,withdrawpub,amount) == 'W' || refcoin!=coin) + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("completesigningtxid",uint256_str(str,txid))); + obj.push_back(Pair("withdrawtxid",uint256_str(str,withdrawtxid))); + CCCustomtxidaddr(txidaddr,withdrawtxid,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawtxidaddr",txidaddr)); + GetCustomscriptaddress(withaddr,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG,taddr,prefix,prefix2); + obj.push_back(Pair("withdrawaddr",withaddr)); + obj.push_back(Pair("confirmed_or_notarized",komodo_txnotarizedconfirmed(txid))); + sprintf(numstr,"%.8f",(double)tx.vout[1].nValue/COIN); + obj.push_back(Pair("amount",numstr)); + obj.push_back(Pair("hex",hex)); + processed.push_back(obj); + } + } + } + result.push_back(Pair("coin",refcoin)); + result.push_back(Pair("processed",processed)); + result.push_back(Pair("queueflag",queueflag)); + return(result); +} + +UniValue ImportGatewayList() +{ + UniValue result(UniValue::VARR); std::vector > addressIndex; + struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction vintx; std::string coin; + char str[65],burnaddr[64]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector pubkeys; + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + SetCCtxids(addressIndex,cp->unspendableCCaddr,true); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + { + if ( vintx.vout.size() > 0 && DecodeImportGatewayBindOpRet(burnaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 ) + { + result.push_back(uint256_str(str,txid)); + } + } + } + return(result); +} + +UniValue ImportGatewayExternalAddress(uint256 bindtxid,CPubKey pubkey) +{ + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction tx; + std::string coin; int64_t numvouts; char str[65],addr[65],burnaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + GetCustomscriptaddress(addr,CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG,taddr,prefix,prefix2); + result.push_back(Pair("result","success")); + result.push_back(Pair("address",addr)); + return(result); +} + +UniValue ImportGatewayDumpPrivKey(uint256 bindtxid,CKey key) +{ + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid; CTransaction tx; + std::string coin,priv; int64_t numvouts; char str[65],addr[65],burnaddr[65]; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + + priv=EncodeCustomSecret(key,wiftype); + result.push_back(Pair("result","success")); + result.push_back(Pair("privkey",priv.c_str())); + return(result); +} + +UniValue ImportGatewayInfo(uint256 bindtxid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; char str[67],numstr[65],burnaddr[64],gatewaystokens[64]; + uint8_t M,N; std::vector pubkeys; uint8_t taddr,prefix,prefix2,wiftype; uint256 oracletxid,hashBlock; CTransaction tx; + CPubKey ImportGatewaypk; struct CCcontract_info *cp,C; int32_t i; int64_t numvouts,remaining; std::vector msigpubkeys; + + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + ImportGatewaypk = GetUnspendable(cp,0); + GetTokensCCaddress(cp,gatewaystokens,ImportGatewaypk); + if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 ) + { + CCerror = strprintf("cant find bindtxid %s",uint256_str(str,bindtxid)); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( DecodeImportGatewayBindOpRet(burnaddr,tx.vout[numvouts-1].scriptPubKey,coin,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2,wiftype) != 'B') + { + CCerror = strprintf("invalid bindtxid %s coin.%s",uint256_str(str,bindtxid),coin.c_str()); + LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("name","ImportGateway")); + burnaddr[0] = 0; + if ( tx.vout.size() > 0 && DecodeImportGatewayBindOpRet(burnaddr,tx.vout[tx.vout.size()-1].scriptPubKey,coin,oracletxid,M,N,pubkeys,taddr,prefix,prefix2,wiftype) != 0 && M <= N && N > 0 ) + { + result.push_back(Pair("M",M)); + result.push_back(Pair("N",N)); + for (i=0; i #include diff --git a/src/cc/lotto.cpp b/src/cc/lotto.cpp index e9c263ff1..f873b3881 100644 --- a/src/cc/lotto.cpp +++ b/src/cc/lotto.cpp @@ -166,7 +166,7 @@ int64_t AddLottoInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubK char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -209,7 +209,7 @@ int64_t LottoPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,u std::vector > unspentOutputs; lockedfunds = 0; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -266,7 +266,7 @@ UniValue LottoList() { UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,hentropy; CTransaction vintx; uint64_t sbits; int32_t ticketsize,odds,firstheight,period; char str[65]; cp = CCinit(&C,EVAL_LOTTO); - SetCCtxids(addressIndex,cp->normaladdr); + SetCCtxids(addressIndex,cp->normaladdr,true); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/makecclib b/src/cc/makecclib index 47aec377d..b2f8e2ee1 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -1 +1,19 @@ -gcc -std=c++11 -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o ../cclib.so cclib.cpp +#!/bin/sh +rm *.so rogue/rogue games/tetris games/prices + +echo rogue +make -f Makefile_rogue +./makerogue + +echo sudoku/musig/dilithium +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o sudokucc.so cclib.cpp + +echo games tetris +./maketetris + +echo games prices +./makeprices + +echo customcc stub +gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp + diff --git a/src/cc/makecustom b/src/cc/makecustom new file mode 100755 index 000000000..61b251e6e --- /dev/null +++ b/src/cc/makecustom @@ -0,0 +1,7 @@ +#!/bin/sh +gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp +cp customcc.so ../libcc.so +cd .. +make +cd cc + diff --git a/src/cc/makegames b/src/cc/makegames new file mode 100755 index 000000000..b4b8cb803 --- /dev/null +++ b/src/cc/makegames @@ -0,0 +1,7 @@ +#!/bin/sh +gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp +cp gamescc.so ../libcc.so +cd .. +make +cd cc + diff --git a/src/cc/makeprices b/src/cc/makeprices new file mode 100755 index 000000000..2779c1c5b --- /dev/null +++ b/src/cc/makeprices @@ -0,0 +1,8 @@ +echo pricescc.so +gcc -O3 -DBUILD_GAMESCC -DBUILD_PRICES -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o pricescc.so cclib.cpp +echo prices +cd games +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE -DBUILD_PRICES ../gamescc.cpp -lncurses -lcurl -o prices +cd .. + + diff --git a/src/cc/makerogue b/src/cc/makerogue new file mode 100755 index 000000000..ff16cbb16 --- /dev/null +++ b/src/cc/makerogue @@ -0,0 +1,37 @@ +#!/bin/sh +cd rogue; +make clean; + +if [ "$HOST" = "x86_64-w64-mingw32" ]; then + echo building rogue.exe... + ./configure --host=x86_64-w64-mingw32 + echo $PWD + wget https://github.com/KomodoPlatform/rogue/releases/download/0.3.3b-01/x86_64-w64-mingw32.tar.gz + tar xvfz x86_64-w64-mingw32.tar.gz && rm x86_64-w64-mingw32.tar.gz + echo lib archive cleaned + echo $PWD + if make -f Makefile_win "$@"; then + echo rogue.exe build SUCCESSFUL + cd .. + else + echo rogue.exe build FAILED + exit 1 + fi +else + echo building rogue... + ./configure + if make "$@"; then + echo rogue build SUCCESSFUL + cd .. + else + echo rogue build FAILED + exit 1 + fi +fi + +if make -f Makefile_rogue "$@"; then + echo ROGUE BUILD SUCCESSFUL +else + echo ROGUE BUILD FAILED + exit 1 +fi diff --git a/src/cc/maketetris b/src/cc/maketetris new file mode 100755 index 000000000..c39536229 --- /dev/null +++ b/src/cc/maketetris @@ -0,0 +1,7 @@ +echo gamescc.so with tetris +gcc -O3 -DBUILD_GAMESCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o gamescc.so cclib.cpp +echo tetris dapp +cd games +gcc -O3 -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -DSTANDALONE ../gamescc.cpp -lncurses -lcurl -o tetris +cd .. + diff --git a/src/cc/marmara.cpp b/src/cc/marmara.cpp index 0e301ba97..f0d55eaf4 100644 --- a/src/cc/marmara.cpp +++ b/src/cc/marmara.cpp @@ -167,7 +167,7 @@ int32_t MarmaraGetbatontxid(std::vector &creditloop,uint256 &batontxid, creditloop.push_back(txid); //fprintf(stderr,"%d: %s\n",n,txid.GetHex().c_str()); n++; - if ( (value= CCgettxout(spenttxid,vout,1)) == 10000 ) + if ( (value= CCgettxout(spenttxid,vout,1,1)) == 10000 ) { batontxid = spenttxid; //fprintf(stderr,"got baton %s %.8f\n",batontxid.GetHex().c_str(),(double)value/COIN); @@ -352,7 +352,7 @@ int64_t AddMarmaraCoinbases(struct CCcontract_info *cp,CMutableTransaction &mtx, std::vector > unspentOutputs; Marmarapk = GetUnspendable(cp,0); GetCCaddress1of2(cp,coinaddr,Marmarapk,poolpk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); unlocks = MarmaraUnlockht(firstheight); //fprintf(stderr,"check coinaddr.(%s)\n",coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) @@ -366,7 +366,7 @@ int64_t AddMarmaraCoinbases(struct CCcontract_info *cp,CMutableTransaction &mtx, { if ( DecodeMaramaraCoinbaseOpRet(vintx.vout[1].scriptPubKey,pk,ht,unlockht) == 'C' && unlockht == unlocks && pk == poolpk && ht >= firstheight ) { - if ( (nValue= vintx.vout[vout].nValue) > 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( (nValue= vintx.vout[vout].nValue) > 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -387,15 +387,19 @@ int64_t AddMarmarainputs(CMutableTransaction &mtx,std::vector &pubkeys, { uint64_t threshold,nValue,totalinputs = 0; uint256 txid,hashBlock; CTransaction tx; int32_t numvouts,ht,unlockht,vout,i,n = 0; uint8_t funcid; CPubKey pk; std::vector vals; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + SetCCunspents(unspentOutputs,coinaddr,true); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; if ( it->second.satoshis < threshold ) continue; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( (funcid= DecodeMaramaraCoinbaseOpRet(tx.vout[numvouts-1].scriptPubKey,pk,ht,unlockht)) == 'C' || funcid == 'P' || funcid == 'L' ) { @@ -437,7 +441,7 @@ UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height) mypk = pubkey2pk(Mypubkey()); Marmarapk = GetUnspendable(cp,0); Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG); - if ( (val= CCaddress_balance(coinaddr)) < amount ) + if ( (val= CCaddress_balance(coinaddr,0)) < amount ) val -= txfee; else val = amount; if ( val > txfee ) @@ -453,16 +457,18 @@ UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height) remains = (amount + txfee) - inputsum; std::vector > unspentOutputs; GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); threshold = remains / (MARMARA_VINS+1); - CCaddr1of2set(cp,Marmarapk,mypk,coinaddr); + uint8_t mypriv[32]; + Myprivkey(mypriv); + CCaddr1of2set(cp,Marmarapk,mypk,mypriv,coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; if ( (nValue= it->second.satoshis) < threshold ) continue; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 && vout < numvouts && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( (funcid= DecodeMaramaraCoinbaseOpRet(tx.vout[numvouts-1].scriptPubKey,pk,ht,unlockht)) == 'C' || funcid == 'P' || funcid == 'L' ) { @@ -660,7 +666,7 @@ int32_t MarmaraGetCreditloops(int64_t &totalamount,std::vector &issuanc std::vector > unspentOutputs; Marmarapk = GetUnspendable(cp,0); GetCCaddress(cp,coinaddr,Marmarapk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); // do all txid, conditional on spent/unspent //fprintf(stderr,"check coinaddr.(%s)\n",coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) @@ -1053,16 +1059,16 @@ UniValue MarmaraInfo(CPubKey refpk,int32_t firstheight,int32_t lastheight,int64_ result.push_back(Pair("result","success")); Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(Mypubkey())) << OP_CHECKSIG); result.push_back(Pair("myaddress",coinaddr)); - result.push_back(Pair("normal",ValueFromAmount(CCaddress_balance(coinaddr)))); + result.push_back(Pair("normal",ValueFromAmount(CCaddress_balance(coinaddr,0)))); GetCCaddress1of2(cp,coinaddr,Marmarapk,Mypubkey()); result.push_back(Pair("myCCactivated",coinaddr)); - result.push_back(Pair("activated",ValueFromAmount(CCaddress_balance(coinaddr)))); + result.push_back(Pair("activated",ValueFromAmount(CCaddress_balance(coinaddr,1)))); result.push_back(Pair("activated16",ValueFromAmount(AddMarmarainputs(mtx,pubkeys,coinaddr,0,MARMARA_VINS)))); GetCCaddress(cp,coinaddr,Mypubkey()); result.push_back(Pair("myCCaddress",coinaddr)); - result.push_back(Pair("CCutxos",ValueFromAmount(CCaddress_balance(coinaddr)))); + result.push_back(Pair("CCutxos",ValueFromAmount(CCaddress_balance(coinaddr,1)))); if ( refpk.size() == 33 ) result.push_back(Pair("issuer",HexStr(refpk))); diff --git a/src/cc/musig.cpp b/src/cc/musig.cpp new file mode 100644 index 000000000..bc113bc5c --- /dev/null +++ b/src/cc/musig.cpp @@ -0,0 +1,868 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +/* first make a combined pk: + +./komodo-cli -ac_name=MUSIG cclib combine 18 '["02fb6aa0b96cad24d46b5da93eba3864c45ce07a73bba12da530ae841e140fcf28","0255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4"]' +{ + "pkhash": "5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b", + "combined_pk": "03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b", + "result": "success" +} + + the combined_pk and pkhash will be needed for various other rpc calls + + second, send 1 coin to the combined_pk + ./komodo-cli -ac_name=MUSIG cclib send 18 '["03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b",1]' + { + "hex": "0400008085202f8901a980664dffc810725a79ffb89ac48be4c7b6bade9b789732fcf871acf8e81a2e010000006a47304402207e52763661ecd2c34a65d6623950be11794825db71576dc11894c606ddc317800220028fef46dc20630d0fdf22647b5d4ff0f1c47cf75f48702d0a91d5589eff99d001210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ffffffff031008f60500000000302ea22c8020c71ddb3aac7f9b9e4bdacf032aaa8b8e4433c4ff9f8a43cebb9c1f5da96928a48103120c008203000401cce09aa4350000000023210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac0000000000000000266a2412782103f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b00000000920500000000000000000000000000", + "txid": "5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c", + "result": "success" + } + + sendrawtransaction of the above hex. + ./komodo-cli -ac_name=MUSIG getrawtransaction 5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c 1 + "vout": [ + { + "value": 1.00010000, + "valueSat": 100010000, + "n": 0, + "scriptPubKey": { + "asm": "a22c8020c71ddb3aac7f9b9e4bdacf032aaa8b8e4433c4ff9f8a43cebb9c1f5da96928a48103120c008203000401 OP_CHECKCRYPTOCONDITION", + "hex": "2ea22c8020c71ddb3aac7f9b9e4bdacf032aaa8b8e4433c4ff9f8a43cebb9c1f5da96928a48103120c008203000401cc", + "reqSigs": 1, + "type": "cryptocondition", + "addresses": [ + "RKWS7jxyjPX9iaJttk8iMKf1AumanKypez" + ] + } + }, + { + "value": 8.99980000, + "valueSat": 899980000, + "n": 1, + "scriptPubKey": { + "asm": "0255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4 OP_CHECKSIG", + "hex": "210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac", + "reqSigs": 1, + "type": "pubkey", + "addresses": [ + "RVQjvGdRbYLJ49bfH4SAFseipvwE3UdoDw" + ] + } + + script: 210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac + + sendtxid: 5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c + + get the msg we need to sign: + + ./komodo-cli -ac_name=MUSIG cclib calcmsg 18 '["5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c","210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac"]' + + { + "msg": "f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75", + "result": "success" + } + +the "msg" is what needs to be signed to create a valid spend + + now on each signing node, a session needs to be created: + 5 args: ind, numsigners, combined_pk, pkhash, message to be signed + + on node with pubkey: 02fb6aa0b96cad24d46b5da93eba3864c45ce07a73bba12da530ae841e140fcf28 + ./komodo-cli -ac_name=MUSIG cclib session 18 '[0,2,"03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b","5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75"]' + { + "myind": 0, + "numsigners": 2, + "commitment": "bbea1f2562eca01b9a1393c5dc188bdd44551aebf684f4459930f59dde01f7ae", + "result": "success" + } + + on node with pubkey: 0255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4 + ./komodo-cli -ac_name=MUSIG cclib session 18 '[1,2,"03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b","5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75"]' + { + "myind": 1, + "numsigners": 2, + "commitment": "c2291acb747a75b1a40014d8eb0cc90a1360f74d413f65f78e20a7de45eda851", + "result": "success" + } + + now we need to get the commitment from each node to the other one. the session already put the commitment for each node into the global struct. Keep in mind there is a single global struct with session unique to each cclib session call. that means no restarting any deamon in the middle of the process on any of the nodes and only call cclib session a single time. this is an artificial restriction just to simplify the initial implementation of musig + ./komodo-cli -ac_name=MUSIG cclib commit 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","1","c2291acb747a75b1a40014d8eb0cc90a1360f74d413f65f78e20a7de45eda851"]' + { + "added_index": 1, + "myind": 0, + "nonce": "02fec7a9310c959a0a97b86bc3f8c30d392d1fb51793915898c568f73f1f70476b", + "result": "success" + } + + ./komodo-cli -ac_name=MUSIG cclib commit 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b",0,"d242cff13fa8c9b83248e4219fda459ada146b885f2171481f1b0f66c66d94ad"]' + { + "added_index": 0, + "myind": 1, + "nonce": "039365deaaaea089d509ba4c9f846de2baf4aa04cf6b26fa2c1cd818553e47f80c", + "result": "success" + } + + Now exchange the revealed nonces to each node: + ./komodo-cli -ac_name=MUSIG cclib nonce 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","1","039365deaaaea089d509ba4c9f846de2baf4aa04cf6b26fa2c1cd818553e47f80c"]' +{ + "added_index": 1, + "myind": 0, + "partialsig": "1d65c09cd9bffe4f0604227e66cd7cd221480bbb08262fe885563a9df7cf8f5b", + "result": "success" +} + +./komodo-cli -ac_name=MUSIG cclib nonce 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b",0,"02fec7a9310c959a0a97b86bc3f8c30d392d1fb51793915898c568f73f1f70476b"]' +{ + "added_index": 0, + "myind": 1, + "partialsig": "4a3795e6801b355102c617390cf5a462061e082e35dc2ed8f8b1fab54cc0769e", + "result": "success" +} + + Almost there! final step is to exchange the partial sigs between signers + ./komodo-cli -ac_name=MUSIG cclib partialsig 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b","1","4a3795e6801b355102c617390cf5a462061e082e35dc2ed8f8b1fab54cc0769e"]' + { + "added_index": 1, + "result": "success", + "combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9" + } + + ./komodo-cli -ac_name=MUSIG cclib partialsig 18 '["5cb5a225064ca6ffc1438cb2a6ac2ac65fe2d5055dc7f6c7ebffb9a231f8912b",0,"1d65c09cd9bffe4f0604227e66cd7cd221480bbb08262fe885563a9df7cf8f5b"]' + { + "added_index": 0, + "result": "success", + "combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9" + } + + Notice both nodes generated the same combined signature! + + Now for a sanity test, we can use the verify call to make sure this sig will work with the msg needed for the spend: + + ./komodo-cli -ac_name=MUSIG cclib verify 18 '["f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75","03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b","a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9"]' + { + "msg": "f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75", + "combined_pk": "03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b", + "combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9", + "result": "success" + } + + and finally the spend: sendtxid, scriptPubKey, musig + + ./komodo-cli -ac_name=MUSIG cclib spend 18 '["5ce74037a153ee210413b48d4e88638b99825a2de1a1f1aa0d36ebf93019824c","210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac","a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9"]' +{ + "scriptpubkey": "210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac", + "msg": "f7fb85d1412814e3c2f98b990802af6ee33dad368c6ba05c2050e9e5506fcd75", + "combined_pk": "03f016c348437c7422eed92d865aa9789614f75327cada463eefc566126b54785b", + "combinedsig": "a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f9", + "hex": "0400008085202f89014c821930f9eb360daaf1a1e12d5a82998b63884e8db4130421ee53a13740e75c000000007b4c79a276a072a26ba067a5658021032d29d6545a2aafad795d9cf50912ecade549137 +163934dfb2895ebc0e211ce8a81409671a60db89b3bc58966f3acc80194479b1a43d868e95a11ebc5609646d18710341a8ff92a7817571980307f5d660cc00a2735ac6333e0a7191243f1263f1959a100af03800112 +a10001ffffffff0200e1f5050000000023210255c46dbce584e3751081b39d7fc054fc807100557e73fc444481618b5706afb4ac0000000000000000686a4c6512792103f016c348437c7422eed92d865aa9789614f +75327cada463eefc566126b54785b40a76f2790747ed2436a281f2660bdbee21bad9ee130b9cab6e542fa618fba1512679d568359db33a008ca39b773c32134276613e93e025ec17e083553449005f900000000a805 +00000000000000000000000000", + "txid": "910635bf69a047fc90567a83ff12e47b753f470658b6d0855ec96e07e7349a8a", + "result": "success" +} +*/ + + +#define USE_BASIC_CONFIG +#define ENABLE_MODULE_MUSIG +#include "../secp256k1/src/basic-config.h" +#include "../secp256k1/include/secp256k1.h" +#include "../secp256k1/src/ecmult.h" +#include "../secp256k1/src/ecmult_gen.h" + +typedef struct { unsigned char data[64]; } secp256k1_schnorrsig; +struct secp256k1_context_struct { + secp256k1_ecmult_context ecmult_ctx; + secp256k1_ecmult_gen_context ecmult_gen_ctx; + secp256k1_callback illegal_callback; + secp256k1_callback error_callback; +}; + + +//#include "../secp256k1/include/secp256k1.h" +//#include "../secp256k1/include/secp256k1_schnorrsig.h" +#include "../secp256k1/include/secp256k1_musig.h" + + +extern "C" int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); +extern "C" int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_pubkey *pk); +extern "C" int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64); +extern "C" int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_pubkey *combined_pk, unsigned char *pk_hash32, const secp256k1_pubkey *pubkeys, size_t n_pubkeys); +extern "C" int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, size_t n_signers, size_t my_index, const unsigned char *seckey); +extern "C" int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig); + +#define MUSIG_PREVN 0 // for now, just use vout0 for the musig output +#define MUSIG_TXFEE 10000 + +struct musig_info +{ + secp256k1_musig_session session; + secp256k1_pubkey combined_pk; + uint8_t *nonce_commitments,**commitment_ptrs; // 32*N_SIGNERS + secp256k1_musig_session_signer_data *signer_data; //[N_SIGNERS]; + secp256k1_pubkey *nonces; //[N_SIGNERS]; + secp256k1_musig_partial_signature *partial_sig; //[N_SIGNERS]; + int32_t myind,num,numcommits,numnonces,numpartials; + uint8_t msg[32],pkhash[32],combpk[33]; +}; + +std::vector MUSIG; + +struct musig_info *musig_infocreate(int32_t myind,int32_t num) +{ + int32_t i; struct musig_info *mp = (struct musig_info *)calloc(1,sizeof(*mp)); + mp->myind = myind, mp->num = num; + mp->nonce_commitments = (uint8_t *)calloc(num,32); + mp->commitment_ptrs = (uint8_t **)calloc(num,sizeof(*mp->commitment_ptrs)); + for (i=0; icommitment_ptrs[i] = &mp->nonce_commitments[i*32]; + mp->signer_data = (secp256k1_musig_session_signer_data *)calloc(num,sizeof(*mp->signer_data)); + mp->nonces = (secp256k1_pubkey *)calloc(num,sizeof(*mp->nonces)); + mp->partial_sig = (secp256k1_musig_partial_signature *)calloc(num,sizeof(*mp->partial_sig)); + return(mp); +} + +void musig_infofree(struct musig_info *mp) +{ + if ( mp->partial_sig != 0 ) + { + GetRandBytes((uint8_t *)mp->partial_sig,mp->num*sizeof(*mp->partial_sig)); + free(mp->partial_sig); + } + if ( mp->nonces != 0 ) + { + GetRandBytes((uint8_t *)mp->nonces,mp->num*sizeof(*mp->nonces)); + free(mp->nonces); + } + if ( mp->signer_data != 0 ) + { + GetRandBytes((uint8_t *)mp->signer_data,mp->num*sizeof(*mp->signer_data)); + free(mp->signer_data); + } + if ( mp->nonce_commitments != 0 ) + { + GetRandBytes((uint8_t *)mp->nonce_commitments,mp->num*32); + free(mp->nonce_commitments); + } + if ( mp->commitment_ptrs != 0 ) + { + GetRandBytes((uint8_t *)mp->commitment_ptrs,mp->num*sizeof(*mp->commitment_ptrs)); + free(mp->commitment_ptrs); + } + GetRandBytes((uint8_t *)mp,sizeof(*mp)); + free(mp); +} + +CScript musig_sendopret(uint8_t funcid,CPubKey pk) +{ + CScript opret; uint8_t evalcode = EVAL_MUSIG; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk); + return(opret); +} + +uint8_t musig_sendopretdecode(CPubKey &pk,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk) != 0 && e == EVAL_MUSIG && f == 'x' ) + { + return(f); + } + return(0); +} + +CScript musig_spendopret(uint8_t funcid,CPubKey pk,std::vector musig64) +{ + CScript opret; uint8_t evalcode = EVAL_MUSIG; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << pk << musig64); + return(opret); +} + +uint8_t musig_spendopretdecode(CPubKey &pk,std::vector &musig64,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> musig64) != 0 && e == EVAL_MUSIG && f == 'y' ) + { + return(f); + } + return(0); +} + +int32_t musig_parsepubkey(secp256k1_context *ctx,secp256k1_pubkey &spk,cJSON *item) +{ + char *hexstr; + if ( (hexstr= jstr(item,0)) != 0 && is_hexstr(hexstr,0) == 66 ) + { + CPubKey pk(ParseHex(hexstr)); + if ( secp256k1_ec_pubkey_parse(ctx,&spk,pk.begin(),33) > 0 ) + return(1); + } else return(-1); +} + +int32_t musig_msghash(uint8_t *msg,uint256 prevhash,int32_t prevn,CTxOut vout,CPubKey pk) +{ + CScript data; uint256 hash; int32_t len = 0; + data << E_MARSHAL(ss << prevhash << prevn << vout << pk); + hash = Hash(data.begin(),data.end()); + memcpy(msg,&hash,sizeof(hash)); + return(0); +} + +int32_t musig_prevoutmsg(uint8_t *msg,uint256 sendtxid,CScript scriptPubKey) +{ + CTransaction vintx; uint256 hashBlock; int32_t numvouts; CTxOut vout; CPubKey pk; + memset(msg,0,32); + if ( myGetTransaction(sendtxid,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + if ( musig_sendopretdecode(pk,vintx.vout[numvouts-1].scriptPubKey) == 'x' ) + { + vout.nValue = vintx.vout[MUSIG_PREVN].nValue - MUSIG_TXFEE; + vout.scriptPubKey = scriptPubKey; + return(musig_msghash(msg,sendtxid,MUSIG_PREVN,vout,pk)); + } + } + return(-1); +} + +UniValue musig_calcmsg(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); uint256 sendtxid; int32_t i,zeros=0; uint8_t msg[32]; char *scriptstr,str[65]; int32_t n; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n == 2 ) + { + sendtxid = juint256(jitem(params,0)); + scriptstr = jstr(jitem(params,1),0); + if ( is_hexstr(scriptstr,0) != 0 ) + { + CScript scriptPubKey; + scriptPubKey.resize(strlen(scriptstr)/2); + decode_hex(&scriptPubKey[0],strlen(scriptstr)/2,scriptstr); + musig_prevoutmsg(msg,sendtxid,scriptPubKey); + for (i=0; i<32; i++) + { + sprintf(&str[i<<1],"%02x",msg[i]); + if ( msg[i] == 0 ) + zeros++; + } + str[64] = 0; + if ( zeros != 32 ) + { + result.push_back(Pair("msg",str)); + result.push_back(Pair("result","success")); + return(result); + } else return(cclib_error(result,"null result, make sure params are sendtxid, scriptPubKey")); + } else return(cclib_error(result,"script is not hex")); + } else return(cclib_error(result,"need exactly 2 parameters: sendtxid, scriptPubKey")); + } else return(cclib_error(result,"couldnt parse params")); +} + +UniValue musig_combine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + size_t clen = CPubKey::PUBLIC_KEY_SIZE; + UniValue result(UniValue::VOBJ); CPubKey pk; int32_t i,n; uint8_t pkhash[32]; char *hexstr,str[67]; secp256k1_pubkey combined_pk,spk; std::vector pubkeys; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + //fprintf(stderr,"n.%d args.(%s)\n",n,jprint(params,0)); + for (i=0; i 0 ) + { + if ( secp256k1_ec_pubkey_serialize(ctx,(uint8_t *)pk.begin(),&clen,&combined_pk,SECP256K1_EC_COMPRESSED) > 0 && clen == 33 ) + { + for (i=0; i<32; i++) + sprintf(&str[i<<1],"%02x",pkhash[i]); + str[64] = 0; + result.push_back(Pair("pkhash",str)); + + for (i=0; i<33; i++) + sprintf(&str[i<<1],"%02x",((uint8_t *)pk.begin())[i]); + str[66] = 0; + result.push_back(Pair("combined_pk",str)); + result.push_back(Pair("result","success")); + return(result); + } else return(cclib_error(result,"error serializeing combined_pk")); + } else return(cclib_error(result,"error combining pukbeys")); + } else return(cclib_error(result,"need pubkeys params")); +} + +UniValue musig_session(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + UniValue result(UniValue::VOBJ); int32_t i,n,myind,num,musiglocation; char *pkstr,*pkhashstr,*msgstr; uint8_t session[32],msg[32],pkhash[32],privkey[32],pub33[33]; CPubKey pk; char str[67]; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 5 ) + { + myind = juint(jitem(params,0),0); + num = juint(jitem(params,1),0); + if ( myind < 0 || myind >= num || num <= 0 ) + return(cclib_error(result,"illegal myindex and numsigners")); + if ( n > 5 ) + musiglocation = juint(jitem(params,5),0); + else if ( n == 5 ) + musiglocation = 0; + //printf("number of params.%i musiglocation.%i\n",n,musiglocation); + if ( MUSIG.size() > musiglocation ) + { + for (int i = 0; i < MUSIG.size()-1; i++) + musig_infofree(MUSIG[i]); + MUSIG.clear(); + } + struct musig_info *temp_musig = musig_infocreate(myind,num); + MUSIG.push_back(temp_musig); + if ( musig_parsepubkey(ctx,MUSIG[musiglocation]->combined_pk,jitem(params,2)) < 0 ) + return(cclib_error(result,"error parsing combined_pubkey")); + else if ( cclib_parsehash(MUSIG[musiglocation]->pkhash,jitem(params,3),32) < 0 ) + return(cclib_error(result,"error parsing pkhash")); + else if ( cclib_parsehash(MUSIG[musiglocation]->msg,jitem(params,4),32) < 0 ) + return(cclib_error(result,"error parsing msg")); + Myprivkey(privkey); + GetRandBytes(session,32); + /** Initializes a signing session for a signer + * + * Returns: 1: session is successfully initialized + * 0: session could not be initialized: secret key or secret nonce overflow + * Args: ctx: pointer to a context object, initialized for signing (cannot + * be NULL) + * Out: session: the session structure to initialize (cannot be NULL) + * signers: an array of signers' data to be initialized. Array length must + * equal to `n_signers` (cannot be NULL) + * nonce_commitment32: filled with a 32-byte commitment to the generated nonce + * (cannot be NULL) + * In: session_id32: a *unique* 32-byte ID to assign to this session (cannot be + * NULL). If a non-unique session_id32 was given then a partial + * signature will LEAK THE SECRET KEY. + * msg32: the 32-byte message to be signed. Shouldn't be NULL unless you + * require sharing public nonces before the message is known + * because it reduces nonce misuse resistance. If NULL, must be + * set with `musig_session_set_msg` before signing and verifying. + * combined_pk: the combined public key of all signers (cannot be NULL) + * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be + * NULL) + * n_signers: length of signers array. Number of signers participating in + * the MuSig. Must be greater than 0 and at most 2^32 - 1. + * my_index: index of this signer in the signers array + * seckey: the signer's 32-byte secret key (cannot be NULL) + */ + //fprintf(stderr, "SESSION: struct_size.%li using struct %i\n",MUSIG.size(), musiglocation); + if ( secp256k1_musig_session_initialize(ctx,&MUSIG[musiglocation]->session,MUSIG[musiglocation]->signer_data, &MUSIG[musiglocation]->nonce_commitments[MUSIG[musiglocation]->myind * 32],session,MUSIG[musiglocation]->msg,&MUSIG[musiglocation]->combined_pk,MUSIG[musiglocation]->pkhash,MUSIG[musiglocation]->num,MUSIG[musiglocation]->myind,privkey) > 0 ) + { + memset(session,0,sizeof(session)); + result.push_back(Pair("myind",(int64_t)myind)); + result.push_back(Pair("numsigners",(int64_t)num)); + for (i=0; i<32; i++) + sprintf(&str[i<<1],"%02x",MUSIG[musiglocation]->nonce_commitments[MUSIG[musiglocation]->myind*32 + i]); + str[64] = 0; + if ( n == 5 ) + MUSIG[musiglocation]->numcommits = 1; + result.push_back(Pair("commitment",str)); + result.push_back(Pair("result","success")); + return(result); + } + else + { + memset(session,0,sizeof(session)); + return(cclib_error(result,"couldnt initialize session")); + } + } else return(cclib_error(result,"wrong number of params, need 5: myindex, numsigners, combined_pk, pkhash, msg32")); +} + +UniValue musig_commit(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + size_t clen = CPubKey::PUBLIC_KEY_SIZE; + UniValue result(UniValue::VOBJ); int32_t i,n,ind,myind; uint8_t pkhash[32]; CPubKey pk; char str[67]; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 3 ) + { + if ( n > 3 ) + myind = juint(jitem(params,3),0); + else if ( n == 3 ) + myind = 0; + if ( cclib_parsehash(pkhash,jitem(params,0),32) < 0 ) + return(cclib_error(result,"error parsing pkhash")); + else if ( memcmp(MUSIG[myind]->pkhash,pkhash,32) != 0 ) + return(cclib_error(result,"pkhash doesnt match session pkhash")); + else if ( (ind= juint(jitem(params,1),0)) < 0 || ind >= MUSIG[myind]->num ) + return(cclib_error(result,"illegal ind for session")); + else if ( cclib_parsehash(&MUSIG[myind]->nonce_commitments[ind*32],jitem(params,2),32) < 0 ) + return(cclib_error(result,"error parsing commitment")); + /** Gets the signer's public nonce given a list of all signers' data with commitments + * + * Returns: 1: public nonce is written in nonce + * 0: signer data is missing commitments or session isn't initialized + * for signing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: the signing session to get the nonce from (cannot be NULL) + * signers: an array of signers' data initialized with + * `musig_session_initialize`. Array length must equal to + * `n_commitments` (cannot be NULL) + * Out: nonce: the nonce (cannot be NULL) + * In: commitments: array of 32-byte nonce commitments (cannot be NULL) + * n_commitments: the length of commitments and signers array. Must be the total + * number of signers participating in the MuSig. + */ + result.push_back(Pair("added_index",ind)); + //fprintf(stderr, "COMMIT: struct_size.%li using_struct.%i added_index.%i\n",MUSIG.size(), myind, ind); + MUSIG[myind]->numcommits++; + if ( MUSIG[myind]->numcommits >= MUSIG[myind]->num && secp256k1_musig_session_get_public_nonce(ctx,&MUSIG[myind]->session,MUSIG[myind]->signer_data,&MUSIG[myind]->nonces[MUSIG[myind]->myind],MUSIG[myind]->commitment_ptrs,MUSIG[myind]->num) > 0 ) + { + if ( secp256k1_ec_pubkey_serialize(ctx,(uint8_t *)pk.begin(),&clen,&MUSIG[myind]->nonces[MUSIG[myind]->myind],SECP256K1_EC_COMPRESSED) > 0 && clen == 33 ) + { + for (i=0; i<33; i++) + sprintf(&str[i<<1],"%02x",((uint8_t *)pk.begin())[i]); + str[66] = 0; + if ( n == 3 ) + MUSIG[myind]->numnonces = 1; + result.push_back(Pair("myind",MUSIG[myind]->myind)); + result.push_back(Pair("nonce",str)); + result.push_back(Pair("result","success")); + } else return(cclib_error(result,"error serializing nonce (pubkey)")); + } + else + { + result.push_back(Pair("status","not enough commitments")); + result.push_back(Pair("result","success")); + } + return(result); + } else return(cclib_error(result,"wrong number of params, need 3: pkhash, ind, commitment")); +} + +UniValue musig_nonce(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + UniValue result(UniValue::VOBJ); int32_t i,n,ind,myind; uint8_t pkhash[32],psig[32]; CPubKey pk; char str[67]; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 3 ) + { + if ( n > 3 ) + myind = juint(jitem(params,3),0); + else if ( n == 3 ) + myind = 0; + if ( cclib_parsehash(pkhash,jitem(params,0),32) < 0 ) + return(cclib_error(result,"error parsing pkhash")); + else if ( memcmp(MUSIG[myind]->pkhash,pkhash,32) != 0 ) + return(cclib_error(result,"pkhash doesnt match session pkhash")); + else if ( (ind= juint(jitem(params,1),0)) < 0 || ind >= MUSIG[myind]->num ) + return(cclib_error(result,"illegal ind for session")); + else if ( musig_parsepubkey(ctx,MUSIG[myind]->nonces[ind],jitem(params,2)) < 0 ) + return(cclib_error(result,"error parsing nonce")); + result.push_back(Pair("added_index",ind)); + /** Checks a signer's public nonce against a commitment to said nonce, and update + * data structure if they match + * + * Returns: 1: commitment was valid, data structure updated + * 0: commitment was invalid, nothing happened + * Args: ctx: pointer to a context object (cannot be NULL) + * signer: pointer to the signer data to update (cannot be NULL). Must have + * been used with `musig_session_get_public_nonce` or initialized + * with `musig_session_initialize_verifier`. + * In: nonce: signer's alleged public nonce (cannot be NULL) + */ + MUSIG[myind]->numnonces++; + //fprintf(stderr, "NONCE: struct_size.%li using_struct.%i added_index.%i numnounces.%i num.%i\n",MUSIG.size(), myind, ind, MUSIG[myind]->numnonces, MUSIG[myind]->num); + if ( MUSIG[myind]->numnonces < MUSIG[myind]->num ) + { + result.push_back(Pair("status","not enough nonces")); + result.push_back(Pair("result","success")); + return(result); + } + for (i=0; inum; i++) + { + if ( secp256k1_musig_set_nonce(ctx,&MUSIG[myind]->signer_data[i],&MUSIG[myind]->nonces[i]) == 0 ) + return(cclib_error(result,"error setting nonce")); + } + /** Updates a session with the combined public nonce of all signers. The combined + * public nonce is the sum of every signer's public nonce. + * + * Returns: 1: nonces are successfully combined + * 0: a signer's nonce is missing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: session to update with the combined public nonce (cannot be + * NULL) + * signers: an array of signers' data, which must have had public nonces + * set with `musig_set_nonce`. Array length must equal to `n_signers` + * (cannot be NULL) + * n_signers: the length of the signers array. Must be the total number of + * signers participating in the MuSig. + * Out: nonce_is_negated: a pointer to an integer that indicates if the combined + * public nonce had to be negated. + * adaptor: point to add to the combined public nonce. If NULL, nothing is + * added to the combined nonce. + */ + if ( secp256k1_musig_session_combine_nonces(ctx,&MUSIG[myind]->session,MUSIG[myind]->signer_data,MUSIG[myind]->num,NULL,NULL) > 0 ) + { + if ( secp256k1_musig_partial_sign(ctx,&MUSIG[myind]->session,&MUSIG[myind]->partial_sig[MUSIG[myind]->myind]) > 0 ) + { + if ( secp256k1_musig_partial_signature_serialize(ctx,psig,&MUSIG[myind]->partial_sig[MUSIG[myind]->myind]) > 0 ) + { + for (i=0; i<32; i++) + sprintf(&str[i<<1],"%02x",psig[i]); + str[64] = 0; + result.push_back(Pair("myind",MUSIG[myind]->myind)); + result.push_back(Pair("partialsig",str)); + result.push_back(Pair("result","success")); + if ( n == 3 ) + MUSIG[myind]->numpartials = 1; + return(result); + } else return(cclib_error(result,"error serializing partial sig")); + } else return(cclib_error(result,"error making partial sig")); + } else return(cclib_error(result,"error combining nonces")); + } else return(cclib_error(result,"wrong number of params, need 3: pkhash, ind, nonce")); +} + +UniValue musig_partialsig(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + UniValue result(UniValue::VOBJ); int32_t i,ind,n,myind; uint8_t pkhash[32],psig[32],out64[64]; char str[129]; secp256k1_schnorrsig sig; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( params != 0 && (n= cJSON_GetArraySize(params)) >= 3 ) + { + if ( n > 3 ) + myind = juint(jitem(params,3),0); + else if ( n == 3 ) + myind = 0; + if ( cclib_parsehash(pkhash,jitem(params,0),32) < 0 ) + return(cclib_error(result,"error parsing pkhash")); + else if ( memcmp(MUSIG[myind]->pkhash,pkhash,32) != 0 ) + return(cclib_error(result,"pkhash doesnt match session pkhash")); + else if ( (ind= juint(jitem(params,1),0)) < 0 || ind >= MUSIG[myind]->num ) + return(cclib_error(result,"illegal ind for session")); + else if ( cclib_parsehash(psig,jitem(params,2),32) < 0 ) + return(cclib_error(result,"error parsing psig")); + else if ( secp256k1_musig_partial_signature_parse(ctx,&MUSIG[myind]->partial_sig[ind],psig) == 0 ) + return(cclib_error(result,"error parsing partialsig")); + result.push_back(Pair("added_index",ind)); + //fprintf(stderr, "SIG: struct_size.%li using_struct.%i added_index.%i\n",MUSIG.size(), myind, ind); + MUSIG[myind]->numpartials++; + if ( MUSIG[myind]->numpartials >= MUSIG[myind]->num && secp256k1_musig_partial_sig_combine(ctx,&MUSIG[myind]->session,&sig,MUSIG[myind]->partial_sig,MUSIG[myind]->num) > 0 ) + { + if ( secp256k1_schnorrsig_serialize(ctx,out64,&sig) > 0 ) + { + result.push_back(Pair("result","success")); + for (i=0; i<64; i++) + sprintf(&str[i<<1],"%02x",out64[i]); + str[128] = 0; + result.push_back(Pair("combinedsig",str)); + } else return(cclib_error(result,"error serializing combinedsig")); + } + else + { + if ( secp256k1_musig_partial_signature_serialize(ctx,psig,&MUSIG[myind]->partial_sig[MUSIG[myind]->myind]) > 0 ) + { + result.push_back(Pair("myind",ind)); + for (i=0; i<32; i++) + sprintf(&str[i<<1],"%02x",psig[i]); + str[64] = 0; + result.push_back(Pair("partialsig",str)); + result.push_back(Pair("result","success")); + result.push_back(Pair("status","need more partialsigs")); + } else return(cclib_error(result,"error generating my partialsig")); + } + return(result); + } else return(cclib_error(result,"wrong number of params, need 3: pkhash, ind, partialsig")); +} + +//int testmain(void); +UniValue musig_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + UniValue result(UniValue::VOBJ); int32_t i,n; uint8_t msg[32],musig64[64]; secp256k1_pubkey combined_pk; secp256k1_schnorrsig musig; char str[129]; + //testmain(); + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 3 ) + { + if ( cclib_parsehash(msg,jitem(params,0),32) < 0 ) + return(cclib_error(result,"error parsing pkhash")); + else if ( musig_parsepubkey(ctx,combined_pk,jitem(params,1)) < 0 ) + return(cclib_error(result,"error parsing combined_pk")); + else if ( cclib_parsehash(musig64,jitem(params,2),64) < 0 ) + return(cclib_error(result,"error parsing musig64")); + for (i=0; i<32; i++) + sprintf(&str[i*2],"%02x",msg[i]); + str[64] = 0; + result.push_back(Pair("msg",str)); + result.push_back(Pair("combined_pk",jstr(jitem(params,1),0))); + for (i=0; i<64; i++) + sprintf(&str[i*2],"%02x",musig64[i]); + str[128] = 0; + result.push_back(Pair("combinedsig",str)); + if ( secp256k1_schnorrsig_parse(ctx,&musig,&musig64[0]) > 0 ) + { + if ( secp256k1_schnorrsig_verify(ctx,&musig,msg,&combined_pk) > 0 ) + { + result.push_back(Pair("result","success")); + return(result); + } else return(cclib_error(result,"musig didnt verify")); + } else return(cclib_error(result,"couldnt parse musig64")); + } else return(cclib_error(result,"wrong number of params, need 3: msg, combined_pk, combinedsig")); +} + +// helpers for rpc calls that generate/validate onchain tx + +UniValue musig_rawtxresult(UniValue &result,std::string rawtx) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + //if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + // RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +UniValue musig_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); int32_t n; char *hexstr; std::string rawtx; int64_t amount; CPubKey musigpk,mypk; + if ( txfee == 0 ) + txfee = MUSIG_TXFEE; + mypk = pubkey2pk(Mypubkey()); + musigpk = GetUnspendable(cp,0); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n == 2 && (hexstr= jstr(jitem(params,0),0)) != 0 && is_hexstr(hexstr,0) == 66 ) + { + CPubKey pk(ParseHex(hexstr)); + amount = jdouble(jitem(params,1),0) * COIN + 0.0000000049; + if ( amount >= 3*txfee && AddNormalinputs(mtx,mypk,amount+2*txfee,64) >= amount+2*txfee ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount+txfee,musigpk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,musig_sendopret('x',pk)); + return(musig_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"couldnt find funds or less than 0.0003")); + } else return(cclib_error(result,"must have 2 params: pk, amount")); + } else return(cclib_error(result,"not enough parameters")); +} + +UniValue musig_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey mypk,pk; secp256k1_pubkey combined_pk; char *scriptstr,*musigstr; uint8_t msg[32]; CTransaction vintx; uint256 prevhash,hashBlock; int32_t i,n,numvouts; char str[129]; CTxOut vout; secp256k1_schnorrsig musig; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n == 3 ) + { + prevhash = juint256(jitem(params,0)); + scriptstr = jstr(jitem(params,1),0); + musigstr = jstr(jitem(params,2),0); + if ( is_hexstr(scriptstr,0) != 0 && is_hexstr(musigstr,0) == 128 ) + { + if ( txfee == 0 ) + txfee = MUSIG_TXFEE; + mypk = pubkey2pk(Mypubkey()); + std::vector musig64(ParseHex(musigstr)); + CScript scriptPubKey; + scriptPubKey.resize(strlen(scriptstr)/2); + decode_hex(&scriptPubKey[0],strlen(scriptstr)/2,scriptstr); + if ( myGetTransaction(prevhash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + vout.nValue = vintx.vout[0].nValue - txfee; + vout.scriptPubKey = scriptPubKey; + if ( musig_sendopretdecode(pk,vintx.vout[numvouts-1].scriptPubKey) == 'x' ) + { + if ( secp256k1_schnorrsig_parse((const secp256k1_context *)ctx,&musig,(const uint8_t *)&musig64[0]) > 0 && + secp256k1_ec_pubkey_parse(ctx,&combined_pk,pk.begin(),33) > 0 ) + { + musig_prevoutmsg(msg,prevhash,vout.scriptPubKey); + { + for (i=0; i<32; i++) + sprintf(&str[i*2],"%02x",msg[i]); + str[64] = 0; + result.push_back(Pair("msg",str)); + for (i=0; i<33; i++) + sprintf(&str[i*2],"%02x",((uint8_t *)pk.begin())[i]); + str[66] = 0; + result.push_back(Pair("combined_pk",str)); + for (i=0; i<64; i++) + sprintf(&str[i*2],"%02x",musig64[i]); + str[128] = 0; + result.push_back(Pair("combinedsig",str)); + } + if ( !secp256k1_schnorrsig_verify((const secp256k1_context *)ctx,&musig,(const uint8_t *)msg,(const secp256k1_pubkey *)&combined_pk) ) + { + return(cclib_error(result,"musig didnt validate")); + } + mtx.vin.push_back(CTxIn(prevhash,MUSIG_PREVN)); + mtx.vout.push_back(vout); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,musig_spendopret('y',pk,musig64)); + return(musig_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"couldnt parse pk or musig")); + } else return(cclib_error(result,"couldnt decode send opret")); + } else return(cclib_error(result,"couldnt find vin0")); + } else return(cclib_error(result,"script or musig is not hex")); + } else return(cclib_error(result,"need to have exactly 3 params sendtxid, scriptPubKey, musig")); + } else return(cclib_error(result,"params parse error")); +} + +bool musig_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + static secp256k1_context *ctx; + secp256k1_pubkey combined_pk; CPubKey pk,checkpk; secp256k1_schnorrsig musig; uint256 hashBlock; CTransaction vintx; int32_t numvouts; std::vector musig64; uint8_t msg[32]; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( tx.vout.size() != 2 ) + return eval->Invalid("numvouts != 2"); + else if ( tx.vin.size() != 1 ) + return eval->Invalid("numvins != 1"); + else if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) + return eval->Invalid("illegal normal vin0"); + else if ( myGetTransaction(tx.vin[0].prevout.hash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + if ( musig_sendopretdecode(pk,vintx.vout[numvouts-1].scriptPubKey) == 'x' ) + { + if ( musig_spendopretdecode(checkpk,musig64,tx.vout[tx.vout.size()-1].scriptPubKey) == 'y' ) + { + if ( pk == checkpk ) + { + if ( secp256k1_schnorrsig_parse((const secp256k1_context *)ctx,&musig,(const uint8_t *)&musig64[0]) > 0 && + secp256k1_ec_pubkey_parse(ctx,&combined_pk,pk.begin(),33) > 0 ) + { + musig_prevoutmsg(msg,tx.vin[0].prevout.hash,tx.vout[0].scriptPubKey); + if ( !secp256k1_schnorrsig_verify((const secp256k1_context *)ctx,&musig,(const uint8_t *)msg,(const secp256k1_pubkey *)&combined_pk) ) + return eval->Invalid("failed schnorrsig_verify"); + else return(true); + } else return eval->Invalid("couldnt parse pk or musig"); + } else return eval->Invalid("combined_pk didnt match send opret"); + } else return eval->Invalid("failed decode musig spendopret"); + } else return eval->Invalid("couldnt decode send opret"); + } else return eval->Invalid("couldnt find vin0 tx"); +} diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index 66c0e1b9a..54e9202e1 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -171,7 +171,7 @@ CPubKey OracleBatonPk(char *batonaddr,struct CCcontract_info *cp) if ( ctx == 0 ) ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); Myprivkey(priv); - cp->evalcode2 = EVAL_ORACLES; + cp->unspendableEvalcode2 = EVAL_ORACLES; for (i=0; i<32; i++) cp->unspendablepriv2[i] = (priv[i] ^ cp->CCpriv[i]); while ( secp256k1_ec_seckey_verify(ctx,cp->unspendablepriv2) == 0 ) @@ -197,7 +197,7 @@ int64_t OracleCurrentDatafee(uint256 reforacletxid,char *markeraddr,CPubKey publ { uint256 txid,oracletxid,hashBlock; int64_t datafee=0,dfee; int32_t dheight=0,vout,height,numvouts; CTransaction tx; CPubKey pk; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,markeraddr); + SetCCunspents(unspentOutputs,markeraddr,false); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -260,7 +260,7 @@ uint256 OracleBatonUtxo(uint64_t txfee,struct CCcontract_info *cp,uint256 refora { uint256 txid,oracletxid,hashBlock,btxid,batontxid = zeroid; int64_t dfee; int32_t dheight=0,vout,height,numvouts; CTransaction tx; CPubKey pk; uint8_t *ptr; std::vector vopret,data; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,batonaddr); + SetCCunspents(unspentOutputs,batonaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -294,7 +294,7 @@ uint256 OracleBatonUtxo(uint64_t txfee,struct CCcontract_info *cp,uint256 refora } } } - while ( myIsutxo_spentinmempool(batontxid,1) != 0 ) + while ( myIsutxo_spentinmempool(ignoretxid,ignorevin,batontxid,1) != 0 ) batontxid = myIs_baton_spentinmempool(batontxid,1); return(batontxid); } @@ -306,7 +306,7 @@ uint256 OraclesBatontxid(uint256 reforacletxid,CPubKey refpk) batontxid = zeroid; cp = CCinit(&C,EVAL_ORACLES); CCtxidaddr(markeraddr,reforacletxid); - SetCCunspents(unspentOutputs,markeraddr); + SetCCunspents(unspentOutputs,markeraddr,false); //char str[67]; fprintf(stderr,"markeraddr.(%s) %s\n",markeraddr,pubkey33_str(str,(uint8_t *)&refpk)); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { @@ -525,14 +525,14 @@ int32_t oracleprice_add(std::vector &publishers,CPubKey } else return(0); } -int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format) +/*int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format) { std::vector > unspentOutputs; CTransaction regtx; uint256 hash,txid,oracletxid,batontxid; CPubKey pk; int32_t i,ht,maxheight=0; int64_t datafee,price; char batonaddr[64]; std::vector data; struct CCcontract_info *cp,C; std::vector publishers; std::vector prices; if ( format[0] != 'L' ) return(0); cp = CCinit(&C,EVAL_ORACLES); - SetCCunspents(unspentOutputs,markeraddr); + SetCCunspents(unspentOutputs,markeraddr,false); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -562,7 +562,7 @@ int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char * return(OracleCorrelatedPrice(height,prices)); } return(0); -} +}*/ int64_t IsOraclesvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) { @@ -708,7 +708,7 @@ int64_t AddOracleInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint char coinaddr[64],funcid; int64_t nValue,price,totalinputs = 0; uint256 tmporacletxid,tmpbatontxid,txid,hashBlock; std::vector origpubkey,data; CTransaction vintx; int32_t numvouts,vout,n = 0; std::vector > unspentOutputs; CPubKey tmppk; int64_t tmpnum; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); //fprintf(stderr,"addoracleinputs from (%s)\n",coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { @@ -724,7 +724,7 @@ int64_t AddOracleInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,uint else if (tmporacletxid==oracletxid) { // get valid CC payments - if ( (nValue= IsOraclesvout(cp,vintx,vout)) >= 10000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( (nValue= IsOraclesvout(cp,vintx,vout)) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -746,7 +746,7 @@ int64_t LifetimeOraclesFunds(struct CCcontract_info *cp,uint256 oracletxid,CPubK char coinaddr[64]; CPubKey pk; int64_t total=0,num; uint256 txid,hashBlock,subtxid; CTransaction subtx; std::vector > addressIndex; GetCCaddress(cp,coinaddr,publisher); - SetCCtxids(addressIndex,coinaddr); + SetCCtxids(addressIndex,coinaddr,true); //fprintf(stderr,"scan lifetime of %s\n",coinaddr); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { @@ -971,7 +971,8 @@ UniValue OracleInfo(uint256 origtxid) UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::vector > unspentOutputs; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction regtx,tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector data; + CTransaction regtx,tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; + struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector data; cp = CCinit(&C,EVAL_ORACLES); CCtxidaddr(markeraddr,origtxid); if ( GetTransaction(origtxid,tx,hashBlock,false) == 0 ) @@ -991,7 +992,7 @@ UniValue OracleInfo(uint256 origtxid) result.push_back(Pair("description",description)); result.push_back(Pair("format",format)); result.push_back(Pair("marker",markeraddr)); - SetCCunspents(unspentOutputs,markeraddr); + SetCCunspents(unspentOutputs,markeraddr,false); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -1027,7 +1028,7 @@ UniValue OraclesList() { UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction createtx; std::string name,description,format; char str[65]; cp = CCinit(&C,EVAL_ORACLES); - SetCCtxids(addressIndex,cp->normaladdr); + SetCCtxids(addressIndex,cp->normaladdr,true); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 859a5ba3f..d83cd0bec 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -15,203 +15,906 @@ #include "CCPayments.h" -/* - Payments CC is a catchall CC, supported invoices, zpayments, automated funds allocation, including token based revshare +/* + 0) txidopret <- allocation, scriptPubKey, opret + 1) create <- locked_blocks, minrelease, list of txidopret + 2) fund createtxid amount opretflag to global CC address with opret or txidaddr without + + 3) release amount -> vout[i] will be scriptPubKeys[i] and (amount * allocations[i]) / sumallocations[] (only using vins that have been locked for locked_blocks+). + + 4) info txid -> display parameters, funds + 5) list -> all txids + + First step is to create txids with the info needed in their opreturns. this info is the weight, scriptPubKey and opret if needed. To do that txidopret is used: + + ./c is a script that invokes komodo-cli with the correct -ac_name + + ./c paymentstxidopret \"[9,%222102d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388dac%22]\" -> rawhex with txid 95d9fc8d8a3ef63693c7427e59ff5e177ef63b7345d5f6d6497ac262699a8def + + ./c paymentstxidopret \"[1,%2221039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775ac%22]\" -> rawhex txid 00469695a08b975ceaf7258896abbf1455eb0f383e8a98fc650deace4cbf02a1 + + now we have 2 txid with the required info in the opreturn. one of them has a 9 and the other a 1 for a 90%/10% split. + + ./c paymentscreate \"[0,0,%2295d9fc8d8a3ef63693c7427e59ff5e177ef63b7345d5f6d6497ac262699a8def%22,%2200469695a08b975ceaf7258896abbf1455eb0f383e8a98fc650deace4cbf02a1%22]\" -> created txid 318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a that will be the createtxid that the other rpc calls will use. + + lets see if this appears in the list + + ./c paymentslist -> + { + "result": "success", + "createtxids": [ + "318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a" + ] + } + + It appeared! now lets get more info on it: + ./c paymentsinfo \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22]\" + { + "lockedblocks": 0, + "totalallocations": 10, + "minrelease": 0, + "RWRM36sC8jSctyFZtsu7CyDcHYPdZX7nPZ": 0.00000000, + "REpyKi7avsVduqZ3eimncK4uKqSArLTGGK": 0.00000000, + "totalfunds": 0.00000000, + "result": "success" + } + + There are 2 possible places the funds for this createtxid can be, the first is the special address that is derived from combining the globalCC address with the txidaddr. txidaddr is a non-spendable markeraddress created by converting the txid into a 33 byte pubkey by prefixing 0x02 to the txid. It is a 1of2 address, so it doesnt matter that nobody knows the privkey for this txidaddr. the second address is the global CC address and only utxo to that address with an opreturn containing the createtxid are funds valid for this payments CC createtxid + + next let us add some funds to it. the funds can be to either of the two addresses, controlled by useopret (defaults to 0) + + ./c paymentsfund \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1,0]\" -> txid 28f69b925bb7a21d2a3ba2327e85eb2031b014e976e43f5c2c6fb8a76767b221, which indeed sent funds to RWRM36sC8jSctyFZtsu7CyDcHYPdZX7nPZ without an opreturn and it appears on the payments info. + + ./c paymentsfund \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1,1]\" -> txid cc93330b5c951b724b246b3b138d00519c33f2a600a7c938bc9e51aff6e20e32, which indeed sent funds to REpyKi7avsVduqZ3eimncK4uKqSArLTGGK with an opreturn and it appears on the payments info. + + +./c paymentsrelease \"[%22318d827cc6d8f25f40517e7fb0982e3f707b4aa749d322483fc336686a87b28a%22,1.5]\" -> a8d5dbbb8ee94c05e75c4f3c5221091f59dcb86e0e9c4e1e3d2cf69e6fce6b81 + + it used both fund utxos */ // start of consensus code -int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) +CScript EncodePaymentsTxidOpRet(int64_t allocation,std::vector scriptPubKey,std::vector destopret) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'T' << allocation << scriptPubKey << destopret); + return(opret); +} + +uint8_t DecodePaymentsTxidOpRet(CScript scriptPubKey,int64_t &allocation,std::vector &destscriptPubKey,std::vector &destopret) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> allocation; ss >> destscriptPubKey; ss >> destopret) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'T' ) + return(f); + } + return(0); +} + +CScript EncodePaymentsFundOpRet(uint256 checktxid) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << checktxid); + return(opret); +} + +uint8_t DecodePaymentsFundOpRet(CScript scriptPubKey,uint256 &checktxid) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> checktxid) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'F' ) + return(f); + } + return(0); +} + +CScript EncodePaymentsOpRet(int32_t lockedblocks,int32_t minrelease,int64_t totalallocations,std::vector txidoprets) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'C' << lockedblocks << minrelease << totalallocations << txidoprets); + return(opret); +} + +uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int64_t &totalallocations,std::vector &txidoprets) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> totalallocations; ss >> txidoprets) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'C' ) + return(f); + } + return(0); +} + +int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr) { char destaddr[64]; if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) + if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (cmpaddr[0] == 0 || strcmp(destaddr,cmpaddr) == 0) ) return(tx.vout[v].nValue); } return(0); } -bool PaymentsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) +void pub2createtxid(char *str) { - static uint256 zerohash; - CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) - { - //fprintf(stderr,"vini.%d check mempool\n",i); - if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); - else - { - //fprintf(stderr,"vini.%d check hash and vout\n",i); - if ( hashBlock == zerohash ) - return eval->Invalid("cant Payments from mempool"); - if ( (assetoshis= IsPaymentsvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) - inputs += assetoshis; - } - } + rev[n-2-i] = str[i]; + rev[n-1-i] = str[i+1]; } - for (i=0; iInvalid("mismatched inputs != outputs + txfee"); - } - else return(true); + rev[n] = 0; + strcpy(str,rev); + free(rev); } bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - return eval->Invalid("no validation yet"); - std::vector > txids; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else + // one of two addresses + // change must go to 1of2 txidaddr + // change is/must be in vout[0] + // only 'F' or 1of2 txidaddr can be spent + // all vouts must match exactly + char temp[128], coinaddr[64], txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash; CTransaction tmptx; + int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; + // user marker vout to get the createtxid + if ( tx.vout.size() < 2 ) + return(eval->Invalid("not enough vouts")); + if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) { - for (i=0; i 0 && DecodePaymentsOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { - if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + return(eval->Invalid("negative values")); + if ( !CheckTxFee(tx, PAYMENTS_TXFEE, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) + return eval->Invalid("txfee is too high"); + Paymentspk = GetUnspendable(cp,0); + //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); + + // Get all the script pubkeys and allocations + std::vector allocations; + std::vector scriptPubKeys; + int64_t checkallocations = 0; + i = 0; + BOOST_FOREACH(const uint256& txidopret, txidoprets) { - return eval->Invalid("illegal normal vini"); + CTransaction tx0; std::vector scriptPubKey,opret; int64_t allocation; + if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); + allocations.push_back(allocation); + //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); + checkallocations += allocation; + } + i++; } - } - //fprintf(stderr,"check amounts\n"); - if ( PaymentsExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Paymentsget invalid amount\n"); - return false; - } - else - { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Paymentsget validated\n"); - else fprintf(stderr,"Paymentsget invalid\n"); - return(retval); - } - } + + // sanity check to make sure we got all the required info + if ( allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size() ) + return(eval->Invalid("missing data cannot validate")); + + //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); + if ( totalallocations != checkallocations ) + return(eval->Invalid("allocation missmatch")); + + txidpk = CCtxidaddr(txidaddr,createtxid); + GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); + //fprintf(stderr, "coinaddr.%s\n", coinaddr); + + // make sure change is in vout 0 and is paying to the contract address. + if ( (change= IsPaymentsvout(cp,tx,0,coinaddr)) == 0 ) + return(eval->Invalid("change is in wrong vout or is wrong tx type")); + + // Check vouts go to the right place and pay the right amounts. + int64_t amount = 0, checkamount; int32_t n = 0; + checkamount = tx.GetValueOut() - change - PAYMENTS_TXFEE; + for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) + { + std::string destscriptPubKey = HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()); + std::string voutscriptPubKey = HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()); + if ( destscriptPubKey != voutscriptPubKey ) + { + fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", destscriptPubKey.c_str(), voutscriptPubKey.c_str()); + return(eval->Invalid("pays wrong address")); + } + int64_t test = allocations[n]; + test *= checkamount; + test /= totalallocations; + if ( test != tx.vout[i].nValue ) + { + fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); + return(eval->Invalid("amounts do not match")); + } + amount += tx.vout[i].nValue; + n++; + } + // This is a backup check to make sure there are no extra vouts paying something else! + if ( checkamount != amount ) + return(eval->Invalid("amounts do not match")); + + if ( amount < minrelease*COIN ) + { + fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); + return(eval->Invalid("amount is too small")); + } + + i = 0; + int32_t ht = chainActive.LastTip()->GetHeight(); + BOOST_FOREACH(const CTxIn& vin, tx.vin) + { + CTransaction txin; + if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) + { + // check the vin comes from the CC address's + char destaddr[64]; + Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); + if ( strcmp(destaddr,coinaddr) != 0 ) + { + std::vector scriptPubKey,opret; uint256 checktxid; + if ( txin.vout.size() < 2 || DecodePaymentsFundOpRet(txin.vout[txin.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid ) + { + fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); + return(eval->Invalid("vin is not paymentsCC type")); + } //else fprintf(stderr, "vin.%i opret type txid.%s\n", i, txin.GetHash().ToString().c_str()); + } + // check the chain depth vs locked blcoks requirement. + CBlockIndex* pblockindex = mapBlockIndex[blockhash]; + if ( pblockindex->GetHeight() > ht-lockedblocks ) + { + fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex->GetHeight(), ht-lockedblocks); + return(eval->Invalid("vin not elegible")); + } + } else return(eval->Invalid("cant get vin transaction")); + i++; + } + } else return(eval->Invalid("create transaction cannot decode")); + } else return(eval->Invalid("Could not get contract transaction")); + return(true); } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) +int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t latestheight) { - // add threshold check - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,ht,n = 0; std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + Paymentspk = GetUnspendable(cp,0); + for (iter=0; iter<2; iter++) { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - // no need to prevent dup - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) + if ( iter == 0 ) + GetCCaddress(cp,coinaddr,Paymentspk); + else GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { - if ( (nValue= IsPaymentsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); + if ( vout == 0 && GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; + if ( latestheight != 0 ) + { + if ( (ht= komodo_blockheight(hashBlock)) == 0 ) + { + fprintf(stderr,"null ht\n"); + continue; + } + else if ( ht > latestheight ) + { + fprintf(stderr,"ht.%d > lastheight.%d\n",ht,latestheight); + continue; + } + } + if ( iter == 0 ) + { + std::vector scriptPubKey,opret; + if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() < 2 || DecodePaymentsFundOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,checktxid) != 'F' || checktxid != createtxid ) + { + fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str()); + continue; + } + } + if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + if ( total != 0 && maxinputs != 0 ) + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + nValue = it->second.satoshis; + totalinputs += nValue; + n++; + //fprintf(stderr,"iter.%d %s/v%d %s %.8f\n",iter,txid.GetHex().c_str(),vout,coinaddr,(double)nValue/COIN); + if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + break; + } //else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); } } } return(totalinputs); } -std::string PaymentsGet(uint64_t txfee,int64_t nValue) +UniValue payments_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) { - CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Paymentspk; int64_t inputs,CCchange=0; struct CCcontract_info *cp,C; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash; - cp = CCinit(&C,EVAL_PAYMENTS); - if ( txfee == 0 ) - txfee = 10000; - Paymentspk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddPaymentsInputs(cp,mtx,Paymentspk,nValue+txfee,60)) > 0 ) + CTransaction tx; + if ( rawtx.size() > 0 ) { - if ( inputs > nValue ) - CCchange = (inputs - nValue - txfee); - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,CCchange,Paymentspk)); - mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - fprintf(stderr,"start at %u\n",(uint32_t)time(NULL)); - j = rand() & 0xfffffff; - for (i=0; i<1000000; i++,j++) + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) { - tmpmtx = mtx; - rawhex = FinalizeCCTx(-1LL,cp,tmpmtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_PAYMENTS << (uint8_t)'G' << j)); - if ( (len= (int32_t)rawhex.size()) > 0 && len < 65536 ) - { - len >>= 1; - decode_hex(buf,len,(char *)rawhex.c_str()); - hash = bits256_doublesha256(0,buf,len); - if ( (hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0 ) - { - fprintf(stderr,"found valid txid after %d iterations %u\n",i,(uint32_t)time(NULL)); - return(rawhex); - } - //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]); - } - } - fprintf(stderr,"couldnt generate valid txid %u\n",(uint32_t)time(NULL)); - return(""); - } else fprintf(stderr,"cant find Payments inputs\n"); - return(""); -} - -std::string PaymentsFund(uint64_t txfee,int64_t funds) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,Paymentspk; CScript opret; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_PAYMENTS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - Paymentspk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 ) - { - mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,funds,Paymentspk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - return(""); -} - -UniValue PaymentsInfo() -{ - UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey Paymentspk; struct CCcontract_info *cp,C; int64_t funding; - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Payments")); - cp = CCinit(&C,EVAL_PAYMENTS); - Paymentspk = GetUnspendable(cp,0); - funding = AddPaymentsInputs(cp,mtx,Paymentspk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize payments CCtx")); return(result); } +cJSON *payments_reparse(int32_t *nump,char *jsonstr) +{ + cJSON *params=0; char *newstr; int32_t i,j; + *nump = 0; + if ( jsonstr != 0 ) + { + if ( jsonstr[0] == '"' && jsonstr[strlen(jsonstr)-1] == '"' ) + { + jsonstr[strlen(jsonstr)-1] = 0; + jsonstr++; + } + newstr = (char *)malloc(strlen(jsonstr)+1); + for (i=j=0; jsonstr[i]!=0; i++) + { + if ( jsonstr[i] == '%' && jsonstr[i+1] == '2' && jsonstr[i+2] == '2' ) + { + newstr[j++] = '"'; + i += 2; + } + else if ( jsonstr[i] == '\'' ) + newstr[j++] = '"'; + else newstr[j++] = jsonstr[i]; + } + newstr[j] = 0; + params = cJSON_Parse(newstr); + if ( 0 && params != 0 ) + printf("new.(%s) -> %s\n",newstr,jprint(params,0)); + free(newstr); + *nump = cJSON_GetArraySize(params); + } + return(params); +} + +uint256 payments_juint256(cJSON *obj) +{ + uint256 tmp; bits256 t = jbits256(obj,0); + memcpy(&tmp,&t,sizeof(tmp)); + return(revuint256(tmp)); +} + +int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t len) +{ + char *hexstr; int32_t val; + if ( (hexstr= jstr(item,0)) != 0 && ((val= is_hexstr(hexstr,0)) == len*2 || (val > 0 && len == 0)) ) + { + val >>= 1; + hexdata.resize(val); + decode_hex(&hexdata[0],val,hexstr); + return(0); + } else return(-1); +} + +UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) +{ + int32_t latestheight,nextheight = komodo_nextheight(); + CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations,checkallocations=0,allocation; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( params != 0 && n == 2 ) + { + createtxid = payments_juint256(jitem(params,0)); + amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) + { + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + return(result); + } + latestheight = (nextheight - lockedblocks - 1); + if ( amount < minrelease*COIN ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","amount too smal")); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("minrelease",ValueFromAmount(minrelease*COIN))); + return(result); + } + txidpk = CCtxidaddr(txidaddr,createtxid); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk)); + m = txidoprets.size(); + for (i=0; i scriptPubKey,opret; + vout.nValue = 0; + if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + vout.nValue = allocation; + vout.scriptPubKey.resize(scriptPubKey.size()); + memcpy(&vout.scriptPubKey[0],&scriptPubKey[0],scriptPubKey.size()); + checkallocations += allocation; + if ( opret.size() > 0 ) + { + onlyopret.resize(opret.size()); + memcpy(&onlyopret[0],&opret[0],opret.size()); + numoprets++; + } + } else break; + mtx.vout.push_back(vout); + } + result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( i != m ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidoprets[i]")); + result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); + return(result); + } + else if ( checkallocations != totalallocations ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","totalallocations mismatch")); + result.push_back(Pair("checkallocations",(int64_t)checkallocations)); + result.push_back(Pair("totalallocations",(int64_t)totalallocations)); + if ( params != 0 ) + free_json(params); + return(result); + } + else if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many oprets")); + if ( params != 0 ) + free_json(params); + return(result); + } + newamount = amount; + for (i=0; i= newamount+2*PAYMENTS_TXFEE ) + { + std::string rawtx; + if ( (CCchange= (inputsum - newamount - 2*PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) + mtx.vout[0].nValue = CCchange; + mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); + GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); + if ( params != 0 ) + free_json(params); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("newamount",ValueFromAmount(newamount))); + return(payments_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough locked funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt decode paymentscreate txid opret")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find paymentscreate txid")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + +UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); + CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount,totalallocations; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,lockedblocks,minrelease; std::vector txidoprets; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( params != 0 && n > 1 && n <= 3 ) + { + txid = payments_juint256(jitem(params,0)); + amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; + if ( n == 3 ) + useopret = jint(jitem(params,2),0) != 0; + if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid createtxid")); + } + else if ( AddNormalinputs(mtx,mypk,amount+PAYMENTS_TXFEE,60) > 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + if ( useopret == 0 ) + { + txidpk = CCtxidaddr(txidaddr,txid); + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,amount,Paymentspk,txidpk)); + } + else + { + mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk)); + opret = EncodePaymentsFundOpRet(txid); + } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,opret); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + +UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey mypk; std::string rawtx; + std::vector scriptPubKey,opret; int32_t allocation,n,retval0,retval1=0; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + if ( params != 0 && n > 1 && n <= 3 ) + { + allocation = juint(jitem(params,0),0); + retval0 = payments_parsehexdata(scriptPubKey,jitem(params,1),0); + if ( n == 3 ) + retval1 = payments_parsehexdata(opret,jitem(params,2),0); + if ( allocation > 0 && retval0 == 0 && retval1 == 0 && AddNormalinputs(mtx,mypk,PAYMENTS_TXFEE,10) > 0 ) + { + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsTxidOpRet(allocation,scriptPubKey,opret)); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid params or cant find txfee")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + result.push_back(Pair("n",(int64_t)n)); + fprintf(stderr,"(%s) %p\n",jsonstr,params); + } + if ( params != 0 ) + free_json(params); + return(result); +} + +UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease; std::string rawtx; int64_t totalallocations = 0; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n >= 4 ) + { + lockedblocks = juint(jitem(params,0),0); + minrelease = juint(jitem(params,1),0); + if ( lockedblocks < 0 || minrelease < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + for (i=0; i scriptPubKey,opret; int64_t allocation; + if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + totalallocations += allocation; + if ( opret.size() > 0 ) + numoprets++; + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidopret")); + result.push_back(Pair("txid",txidoprets[i].GetHex())); + result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); + return(result); + } + } + if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many opreturns")); + result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( params != 0 ) + free_json(params); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough normal funds")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + +UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) +{ + // need to code: exclude list of tokenid, dust threshold, maxpayees, excluded pubkeys[] + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease; std::string rawtx; int64_t totalallocations = 0; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n >= 4 ) + { + lockedblocks = juint(jitem(params,0),0); + minrelease = juint(jitem(params,1),0); + if ( lockedblocks < 0 || minrelease < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + for (i=0; i scriptPubKey,opret; int64_t allocation; + if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + totalallocations += allocation; + if ( opret.size() > 0 ) + numoprets++; + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidopret")); + result.push_back(Pair("txid",txidoprets[i].GetHex())); + result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); + return(result); + } + } + if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many opreturns")); + result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( params != 0 ) + free_json(params); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough normal funds")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + +UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease; std::vector txidoprets; int64_t funds,fundsopret,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n == 1 ) + { + Paymentspk = GetUnspendable(cp,0); + createtxid = payments_juint256(jitem(params,0)); + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) + { + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); + result.push_back(Pair("totalallocations",(int64_t)totalallocations)); + result.push_back(Pair("minrelease",(int64_t)minrelease)); + for (i=0; i scriptPubKey,opret; + obj.push_back(Pair("txid",txidoprets[i].GetHex())); + if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + outstr = (char *)malloc(2*(scriptPubKey.size() + opret.size()) + 1); + for (j=0; j 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many opreturns")); + } + else + { + result.push_back(Pair("txidoprets",a)); + txidpk = CCtxidaddr(txidaddr,createtxid); + GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); + funds = CCaddress_balance(fundsaddr,1); + result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); + GetCCaddress(cp,fundsopretaddr,Paymentspk); + fundsopret = CCaddress_balance(fundsopretaddr,1); + result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); + result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); + result.push_back(Pair("result","success")); + } + } + } + if ( flag == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find valid payments create txid")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + +UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) +{ + std::vector > addressIndex; uint256 txid,hashBlock; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease; std::vector txidoprets; int64_t totalallocations; + Paymentspk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); + SetCCtxids(addressIndex,markeraddr,true); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + txid = it->first.txhash; + if ( it->first.index == 0 && myGetTransaction(txid,tx,hashBlock) != 0 ) + { + if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' ) + { + if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + return(result); + } + a.push_back(uint256_str(str,txid)); + } + } + } + result.push_back(Pair("result","success")); + result.push_back(Pair("createtxids",a)); + return(result); +} diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index e2893bec8..21cee8ca1 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -16,17 +16,72 @@ #include "CCPegs.h" /* - Pegs CC builds on top of Prices CC and would bind a pricefeed to a token bid/ask automated marketmaking. +pegs CC is able to create a coin backed (by any supported coin with gateways CC deposits) and pegged to any synthetic price that is able to be calculated based on prices CC - Funds deposited into CC address for a specific peg would then be used to fund the bid/ask as the pricefeed changes the price. Profits/losses would accumulate in the associated address. + First, the prices CC needs to be understood, so the extensive comments at the top of ~/src/cc/prices.cpp needs to be understood. - In the event funds exceed a specified level, it can be spent into a collection address. The idea is that the collection address can further be used for revshares. + The second aspect is the ability to import coins, as used by the crosschain burn/import and the -ac_import chains. - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); + + + OK, now we are ready to describe the pegs CC. Let us imagine an -ac_import sidechain with KMD gateways CC. Now we have each native coin fungible with the real KMD via the gateways deposit/withdraw mechanism. Let us start with that and make a pegged and backed USD chain. + + + + Here the native coin is KMD, but we want USD, so there needs to be a way to convert the KMD amounts into USD amounts. Something like "KMDBTC, BTCUSD, *, 1" which is the prices CC syntax to calculate KMD/USD, which is exactly what we need. So now we can assume that we have a block by block usable KMD/USD price. implementationwise, there can be an -ac option like -ac_peg="KMDBTC, BTCUSD, *, 1" and in conjunction with -ac_import=KMD gateways CC sidechain, we now have a chain where deposit of KMD issues the correct USD coins and redeem of USD coins releases the correct number of KMD coins. + + Are we done yet? + + Not quite, as the prices of KMD will be quite volatile relative to USD, which is good during bull markets, not so happy during bear markets. There are 2 halves to this problem, how to deal with massive price increase (easy to solve), how to solve 90% price drop (a lot harder). + + In order to solve both, what is needed is an "account" based tracking which updates based on both price change, coins issued, payments made. So let us create an account that is based on a specific pubkey, where all relevant deposits, issuances, withdraws are tracked via appropriate vin/vout markers. + + Let us modify the USD chain above so that only 80% of the possible USD is issued and 20% held in reserve. This 80% should be at least some easily changeable #define, or even an -ac parameter. We want the issued coins to be released without any encumberances, but the residual 20% value is still controlled (owned) but the depositor. This account has the amount of KMD deposited and USD issued. At the moment of deposit, there will still be 20% equity left. Let us start with 1000 KMD deposit, $1.5 per KMD -> 800 KMD converted to 1200 USD into depositor pubkey and the account of (1000 KMD, -1200 USD) = 200 KMD or $300 equity. + + Now it becomes easy for the bull market case, which is to allow (for a fee like 1%) issuance of more USD as the equity increases, so let us imagine KMD at $10: + + (1000 KMD, -1200 USD, 200KMD reserve) -> $2000 equity, issue 80% -> $1600 using 160 KMD + (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) + + we have $2800 USD in circulation, 40 KMD reserve left against $10000 marketcap of the original deposit. It it easy to see that there are never any problems with lack of KMD to redeem the issued USD in a world where prices only go up. Total USD issuance can be limited by using a decentralized account tracking based on each deposit. + + What is evident though is that with the constantly changing price and the various times that all the various deposits issue USD, the global reserves are something that will be hard to predict and in fact needs to be specifically tracked. Let us combine all accounts exposure in to a global reserves factor. This factor will control various max/min/ allowed and fee percentages. + + Now we are prepared to handle the price goes down scenario. We can rely on the global equity/reserve ratio to be changing relatively slowly as the reference price is the smooted trustless oracles price. This means there will be enough blocks to adjust the global reserves percentage. What we need to do is liquidate specific positions that have the least reserves. + + What does liquidation mean? It means a specific account will be purchased at below its current value and the KMD withdrawn. Let us assume the price drops to $5: + + (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) 1000 KMD with 2800 USD issued so $2200 reserves. Let us assume it can be liquidated at a 10% discount, so for $2000 in addition to the $2800, the 5000 KMD is able to be withdrawn. This removes 4800 USD coins for 1000 KMD, which is a very low reserve amount of 4%. If a low reserve amount is removed from the system, then the global reserve amount must be improved. + + In addition to the global reserves calculation, there needs to be a trigger percentage that enables positions to be liquidated. We also want to liquidate the worst positions, so in addition to the trigger percentage, there should be a liquidation threshold, and the liquidator would need to find 3 or more better positions that are beyond the liquidation threshold, to be able to liquidate. This will get us to at most 3 accounts that could be liquidated but are not able to, so some method to allow those to also be liquidated. The liquidating nodes are making instant profits, so they should be expected to do whatever blockchain scanning and proving to make things easy for the rest of the nodes. + + One last issue is the normal redemption case where we are not liquidating. In this case, it should be done at the current marketprice, should improve the global reserves metrics and not cause anybody whose position was modified to have cause for complaint. Ideally, there would be an account that has the identical to the global reserve percentage and also at the same price as current marketprice, but this is not realistic, so we need to identify classes of accounts and consider which ones can be fully or partially liquidated to satisfy the constraints. + + looking at our example account: + (1000 KMD, -1200 USD, 200KMD reserve, -160KMD, issue $1600 USD, 40 KMD reserve) - OraclePrice is very useful for pegs. + what sort of non-liquidation withdraw would be acceptable? if the base amount 1000 KMD is reduced along with USD owed, then the reserve status will go up for the account. but that would seem to allow extra USD to be able to be issued. there should be no disadvantage from funding a withdraw, but also not any large advantage. it needs to be a neutral event.... -*/ + One solution is to allow for the chance for any account to be liquidated, but the equity compensated for with a premium based on the account reserves. So in the above case, a premium of 5% on the 40KMD reserve is paid to liquidate its account. Instead of 5% premium, a lower 1% can be done if based on the MAX(correlated[daywindow],smoothed) so we get something that is close to the current marketprice. To prevent people taking advantage of the slowness of the smoothed price to adjust, there would need to be a one day delay in the withdraw. + + From a practical sense, it seems a day is a long time, so maybe having a way to pay a premium like 10%, or wait a day to get the MAX(correlated[daywindow],smoothed) price. This price "jumping" might also be taken advantage of in the deposit side, so similar to prices CC it seems good to have the MAX(correlated[daywindow],smoothed) method. + + Now, we have a decentralized mechanism to handle the price going lower! Combined with the fully decentralized method new USD coins are issued, makes this argubably the first decentralized blockchain that is both backed and pegged. There is the reliance on the gateways CC multisig signers, so there is a fundamental federated trust for chains without intrinsic value. + + Also, notice that the flexibly syntax of prices CC allows to define pegs easily for virtually any type of synthetic, and all the ECB fiats can easily get a backed and pegged coin. + + Let us now consider how to enforce a peg onto a specific gateways CC token. If this can also be achieved, then a full DEX for all the different gateways CC supported coins can be created onto a single fiat denominated chain. + + I think just having a pegscreate rpc call that binds an existing gateways create to a price CC syntax price will be almost enough to support this. Let us assume a USD stablechain and we have a BTC token, then pegscreate "BTCUSD, 1" + that will specify using the BTCUSD price, so now we need to create a based way to do tokenbid/tokenask. For a based price, the smoothed price is substituted. + + There is the issue of the one day delay, so it might make sense to allow specific bid/ask to be based on some simple combinations of the three possible prices. it might even be possible to go a bit overboard and make a forth like syntax to define the dynamic price for a bid, which maybe at times wont be valid, like it is only valid if the three prices are within 1% of each other. But all that seems over complex and for initial release it can just use the mined, correlated or smoothed price, with some specified percentage offset + + Implementation notes: + make sure that fees and markers that can be sent to an unspendable address are sent to: RNdqHx26GWy9bk8MtmH1UiXjQcXE4RKK2P, this is the address for BOTS + + + */ // start of consensus code @@ -126,7 +181,7 @@ int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -134,7 +189,7 @@ int64_t AddPegsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKe // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( (nValue= IsPegsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( (nValue= IsPegsvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 369495b1a..5d164d97e 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -13,135 +13,233 @@ * * ******************************************************************************/ +#include "CCassets.h" #include "CCPrices.h" /* - Prices CC would best build on top of the oracles CC, ie. to combine payments for multiple oracles and to calculate a 51% protected price feed. +CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; - We need to assume there is an oracle for a specific price. In the event there are more than one provider, the majority need to be within correlation distance to update a pricepoint. + 0.5% fee based on betamount, NOT leveraged betamount!! + 0.1% collected by price basis determinant + 0.2% collected by rekt tx - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); + At the simplest, prices CC allows to make leveraged cash settled bets on long and short BTCUSD: + BTCUSD, 1 with positive leverage parameter up to 777 + BTCUSD, 1 with negative leverage parameter up to -777 + + These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define + + Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection address (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) + + The definition of the synthetic instrument that is being tracked is actually defined with a forth type of syntax and is quite flexible to allow unlimited combinations and weights, but that is an independent aspect and will be covered later. + + Let us discuss the simplest synthetic case of a long or short of BTCUSD (or any other direct pricepoint from the trustless oracle). If you look at the charts, you will see the blue line that is direct from the miners (and therefore cant be directly used). The orange line is the 51% correlated price that is deterministically random selected from the prior 24 hours. And finally the green line which is simply the average value from the priot 24 hours. + + We will assume that the orange and green prices are able to be deterministically calculated from the raw coinbase data (blue). The prices rpc is currently working reasonably well and appears to return deterministic prices, however for use in the prices CC, it is often needed to find just a single data point. To that effect there is the temporary function prices_syntheticprice, which uses a bruteforce and totally inefficient way to calculate the correlated and smoothed prices. This inefficient process will need to be changed to a permanent storage of correlated and smoothed prices for each trustless oracle that is updated each block. This will then allow to directly lookup each value for any of the trustless prices. Such speed will indeed be needed to scale up usage of a REKT chain. + + Since the mined prices can be manipulated by any miner, combined with the +/-1% tolerance, it would be possible for an external miner to obtain a large hashrate and skew the price +1%, then to -1% to create a 2% price difference, which under high leverage might be able to generate a large profit. + + To avoid this precisely controllable biasing, the 51% correlation of past day is used (orange), which makes the price jump around a lot. The green line that sums the prior day is needed to remove this jitter back to the average value. However, this creates a 1.5 day delay to the price movement of the smoothed price compared to the current price. What this means is that if we want to use the green line to settle prices automatically, a trivial way to make money is to bet in the direct that the mined prices are relative to the smoothed prices. Given 1.5 day head start, it wont be any mystery which direction the smoothed line will move in the next 24 hours. + + So this makes the smoothed price unusable for settling the bets with. However, we cant use the correlated price either, as it would also allow to make bets when the correlated price picked a significantly lower/higher price than the raw price. The solution is to have a floating basis for the costbasis of the bet. In order to allow finding the right costbasis, for long bets the maximum price between the correlated and smoothed price is needed over the first day after the bet. For short bets, the minimum price is needed. + + What this means is that the actual starting price for a bet wont be known for sure when the bet is made, but if the price is relatively stable, there wont be much of a change. If there is a lot of recent volatility, then the starting price will have a high variability. In order to calculate the costbasis, it currently requires many calculations to find the MAX (or MIN) for the first day. Even when this is made to be a lookup, it still requires to issue a transaction to change the state of a bet from a "starting" state to a "in effect" state and this state change is indicated by whether the bettx vout that contains the costbasis utxo is spent or not. + + Once a bet goes into effect, then block by block, it is checked by the decentralized set of rekt scanners if it is rekt or not, in this case for double the reward for calculating the cost basis. This fully decentralized mechanism ensures that some node will decide to collect the free money and ensures that the bankroll is growing. To miss a rekt bet and not close it out when it can be is to allow it to survive for another block, which can change it profitability from negative to positive. + + Which comes to how profits are calculated. Once the costbasis is locked, then the profit calculation can be made based on the ratio between the costbasis and the current price, multiplied by the leverage. So, if a long position gains 10% at a 10x leverage, then approximately the entire bet amount will be made, ie. a double. Similarily with a short position, a 10% drop in price at 10x leverage will double the bet amount. Since it takes a full day to establish the costbasis, it is not possible to allow changing the costbasis for an existing bet, however it is possible to add funds so that it is less likely to be rekt. The sum of the initial bet and all added funds must be greater than the loss at every block to prevent a position from being rekt. To make it simple to calculate the amount of funds added, another bettx vout is used as a baton. Techniques similar to rogue CC to find where the bettx vout was spent and looking at each such transaction to find the total funds added can be used. + + The once that makes the bet is able to add funds or cashout at any block, as long as it isnt rekt. If it is rekt, the bettor is able to collect the rekt fee, so at least that is some consolation, but it is advised to increase the total funding to avoid being rekt. On a cashout all the funds that were bet adjusted by the profits can be collected. + + Hopefully the above description makes it clear what the following rpc calls should be doing: + + UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) + funds are locked into 1of2 global CC address + for first day, long basis is MAX(correlated,smoothed), short is MIN() + reference price is the smoothed of the previous block + if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price + original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund + 0.5% of bet -> globalfund - Using the above function, a consensus price can be obtained for a datasource. + UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) + add funding to an existing bet, doesnt change the profit calcs but does make the bet less likely to be rekt - given an oracletxid, the marketaddr and format can be extracted to be used for future calls to OraclePrice. This allows to set a starting price and that in turn allows cash settled leveraged trading! + UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) + in the first day from the bet, the costbasis can (and usually does) change based on the MAX(correlated,smoothed) for long and MIN() for shorts + to calculate this requires a bit of work, so whatever node is first to get the proper calculation confirmed, gets a 0.1% costbasis fee - Funds work like with dice, ie. there is a Prices plan that traders bet against. + UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) + similarily, any node can submit a rekt tx and cash in on 0.2% fee if their claim is confirmed - PricesFunding oracletxid, margin, priceaveraging, maxleverage, funding, longtoken, shorttoken, N [pubkeys] + UniValue PricesCashout(uint64_t txfee,uint256 bettxid) + only the actually creator of bet is able to cashout and only if it isnt rekt at that moment - PricesBet -> oracletxid start with 'L', leverage, funding, direction - funds are locked into global CC address - it can be closed at anytime by the trader for cash settlement - the house account can close it if rekt + UniValue PricesInfo(uint256 bettxid,int32_t refheight) + all relevant info about a bet - Implementation Notes: - In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. + UniValue PricesList() + a list of all pending and completed bets in different lists - delta neutral balancing of riskexposure: fabs(long exposure - short exposure) - bet +B at leverage L - absval(sum(+BLi) - sum(-Bli)) - validate: update riskexposure and it needs to be <= funds + Appendix Synthetic position definition language: - PricesProfits: limit withdraw to funds in excess of riskexposure - PricesFinish: payout (if winning) and update riskexposure - need long/short exposure assets + Let us start from the familiar BTCUSD nomenclature. this is similar (identical) to forex EURUSD and the equivalent. Notice the price needs two things as it is actually a ratio, ie. BTCUSD is how many USD does 1 BTC get converted to. It can be thought of as the value of BTC/USD, in other words a ratio. - funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address - pricebet -> user funds and exposure token to 1of2 address. - pricewin -> winnings from dealer funds, exposure token back to global address - priceloss -> exposuretoken back to global address + The value of BTC alone, or USD alone, it is actually quite an abstract issue and it can only be approximated. Specific ways of how to do this can be discussed later, for now it is only important to understand that all prices are actually ratios. - exposure address, funds address + And these ratios work as normal algebra does. So a/b * b/c == a/c! You can try this out with BTCUSD and USDJPY -> BTC/USD * USD/JPY -> BTC/JPY or the BTCJPY price + division is the reciprocal, so BTCUSD reciprocated is USDBTC + + Without getting into all the possible combinations of things, let us first understand what the language allows. It uses a stack based language, where individual tokens update the state. The final state needs to have an empty stack and it will have a calculated price. + + The most important is pushing a specific price onto the stack. All the correlated and smoothed prices are normalized so it has each integer unit equal to 0.00000001, amounts above 100 million are above one, amounts less are below one. 100 million is the unit constant. + + In the prices CC synthetic definition language, the symbol that is returned pushes the adjusted price to the stack. The adjustment is selecting between the correlated and smoothed if within a day from the bet creation, or just the smoothed if after a day. You can have a maximum depth of 3, any more than that and it should return an error. + + This means there are operations that are possible on 1, 2 and 3 symbols. For 1 symbol, it is easy, the only direct operation is the inverse, which is represented by "!". All items in the language are separated by "," + + "BTCUSD, !" + + The above is the inverse or USD/BTC, which is another way to short BTCUSD. It is also possible to short the entire synthetic with a negative leverage. The exact results of a -1 leverage and inverse, might not be exact due to the math involved with calculating the profit, but it should generally be similar. + + For two symbols, there is a bit more we can do, namely multiply and divide, combined with inverting, however to simplify the language any inverting is required to be done by the ordering of the symbols and usage of multiply or divide. multiply is "*" and divide is "/" the top of the stack (last to be pushed) is the divisor, the one right before the divisor is the numerator. + + "BTCUSD, USDJPY, *" <- That will create a BTCJPY synthetic + + "BTCEUR, BTCUSD, /" <- That will create a USDEUR synthetic + + If you experiment around with this, you will see that given two symbols and the proper order and * or /, you can always create the synthetic that you want, assuming what you want is the cancelling out of the term in common with the two symbols and the resulting ratio is between the two unique terms. + */ +// Now we get to the three symbol operations, which there are 4 of *//, **/, *** and /// -*/ +/* + these four operators work on the top of the stack in left to right order as the syntax of the definition lists it, though it is even possible to have the value from an earlier computation on the top of the stack. Ultimately all three will be multiplied together, so a * in a spot means it us used without changing. A / means its inverse will be used. + + "KMDBTC, BTCUSD, USDJPY, ***" <- this would create a KMDJPY synthetic. The various location of the / to make an inverse is to orient the raw symbol into the right orientation as the pricefeed is only getting one orientation of the ratio. + + So now we have covered all ways to map 1, 2 and 3 values on the top of the stack. A value can be on the top of the stack directly from a symbol, or as the result of some 1, 2 or 3 symbol operation. With a maximum stack depth of 3, it might require some experimentation to get a very complex synthetic to compile. Alternately, a stack deeper than 3 might be the acceptable solution if there are a family of synthetics that need it. + + At this point, it is time to describe the weights. It turns out that all above examples are incomplete and the synthetic descriptions are all insufficient and should generate an error. The reason is that the actual synthetic price is the value of the accumulator, which adds up all the weighted prices. In order to assign a weight to a price value on the stack, you simply use a number that is less than 2048. + + What such a weight number does, is consume the top of the stack, which also must be at depth of 1 and adds it to the accumulator. This allows combining multiple different synthetics into a meta synthetic, like for an aggregated index that is weighted by marketcap, or whatever other type of ratio trade that is desired. + + "BTCUSD, 1000, ETHBTC, BTCUSD, *, 300" -> that creates a dual index of BTCUSD and ETHUSD with a 30% ETH weight + + all weight operations consumes the one and only stack element and adds its weight to the accumulator, using this a very large number of terms can be all added together. Using inverses, allows to get the short direction into the equation mixed with longs, but all terms must be positive. If you want to create a spread between two synthetics, you need to create two different synthetics and go long with one and short with another. + + "BTCUSD, 1" with leverage -2 and "KMDBTC, BTCUSD, *, 1" with leverage 1 this will setup a +KMDUSD -2 BTCUSD spread when the two are combined, and would be at breakeven when KMDUSD gains 2x more percentage wise than BTC does. anytime KMD gains more, will be positive, if the gains are less, would be negative. + + Believe it or not, the string to binary compiler for synthetic description and interpretation of it is less than 200 lines of code. + + todo: complete all the above, bet tokens, cross chain prices within cluster These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define + + Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection scriptPubKey (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) this requires adding new vouts + + Modification: in the event there is one price in the accumulator and one price on the stack at the end, then it is a (A - B) spread + + Monetizations should be sent to: RGsWqwFviaNJSbmgWi6338NL2tKkY7ZqKL + + */ + // start of consensus code -int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) -{ - int64_t price; - // howto ensure price when block it confirms it not known - // get price from oracle + current chaintip - // normalize leveraged amount - if ( dir > 0 ) - rektprice = price * leverage / (leverage-1); - else rektprice = price * (leverage-1) / leverage; - return(price); -} - -CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) +CScript prices_betopret(CPubKey mypk,int32_t height,int64_t amount,int16_t leverage,int64_t firstprice,std::vector vec,uint256 tokenid) { CScript opret; - fprintf(stderr,"implement EncodePricesFundingOpRet\n"); + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'B' << mypk << height << amount << leverage << firstprice << vec << tokenid); return(opret); } -uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) +uint8_t prices_betopretdecode(CScript scriptPubKey,CPubKey &pk,int32_t &height,int64_t &amount,int16_t &leverage,int64_t &firstprice,std::vector &vec,uint256 &tokenid) { - fprintf(stderr,"implement DecodePricesFundingOpRet\n"); + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B' ) + { + return(f); + } + return(0); +} + +CScript prices_addopret(uint256 bettxid,CPubKey mypk,int64_t amount) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'A' << bettxid << mypk << amount); + return(opret); +} + +uint8_t prices_addopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int64_t &amount) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> pk; ss >> amount) != 0 && e == EVAL_PRICES && f == 'A' ) + { + return(f); + } + return(0); +} + +CScript prices_costbasisopret(uint256 bettxid,CPubKey mypk,int32_t height,int64_t costbasis) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'C' << bettxid << mypk << height << costbasis); + return(opret); +} + +uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> pk; ss >> height; ss >> costbasis) != 0 && e == EVAL_PRICES && f == 'C' ) + { + return(f); + } + return(0); +} + +CScript prices_finalopret(uint256 bettxid,int64_t profits,int32_t height,CPubKey mypk,int64_t firstprice,int64_t costbasis,int64_t addedbets,int64_t positionsize,int16_t leverage) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'F' << bettxid << profits << height << mypk << firstprice << costbasis << addedbets << positionsize << leverage); + return(opret); +} + +uint8_t prices_finalopretdecode(CScript scriptPubKey,uint256 &bettxid,int64_t &profits,int32_t &height,CPubKey &pk,int64_t &firstprice,int64_t &costbasis,int64_t &addedbets,int64_t &positionsize,int16_t &leverage) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> profits; ss >> height; ss >> pk; ss >> firstprice; ss >> costbasis; ss >> addedbets; ss >> positionsize; ss >> leverage) != 0 && e == EVAL_PRICES && f == 'F' ) + { + return(f); + } return(0); } bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - return eval->Invalid("no validation yet"); - std::vector > txids; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Pricesget invalid amount\n"); - return false; - } - //else - { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Pricesget validated\n"); - else fprintf(stderr,"Pricesget invalid\n"); - return(retval); - } - } + return true; } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) +int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,int64_t total,int32_t maxinputs,uint256 vintxid,int32_t vinvout) { - // add threshold check int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,destaddr); + SetCCunspents(unspentOutputs,destaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - // need to prevent dup + if ( vout == vinvout && txid == vintxid ) + continue; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) { - // need to verify assetid - if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( (nValue= vintx.vout[vout].nValue) >= total/maxinputs && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -156,18 +254,470 @@ int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char return(totalinputs); } +UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +int32_t prices_syntheticvec(std::vector &vec,std::vector synthetic) +{ + int32_t i,need,ind,depth = 0; std::string opstr; uint16_t opcode,weight; + if ( synthetic.size() == 0 ) + return(-1); + for (i=0; i= 0 ) + opcode = ind, need = 0; + else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) + { + opcode = PRICES_WEIGHT | weight; + need = 1; + } else return(-2); + if ( depth < need ) + return(-3); + depth -= need; + if ( (opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT ) // weight + depth++; + if ( depth > 3 ) + return(-4); + vec.push_back(opcode); + } + if ( depth != 0 ) + { + fprintf(stderr,"depth.%d not empty\n",depth); + return(-5); + } + return(0); +} + +int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) +{ + int32_t i,ind,errcode,depth,retval = -1; uint16_t opcode; int64_t *pricedata,pricestack[4],price,den,a,b,c; + pricedata = (int64_t *)calloc(sizeof(*pricedata)*3,1 + PRICES_DAYWINDOW*2 + PRICES_SMOOTHWIDTH); + price = den = depth = errcode = 0; + for (i=0; i 0 ) + pricestack[depth] = (pricedata[1] > pricedata[2]) ? pricedata[1] : pricedata[2]; // MAX + else pricestack[depth] = (pricedata[1] < pricedata[2]) ? pricedata[1] : pricedata[2]; // MIN + } + } + if ( pricestack[depth] == 0 ) + errcode = -1; + depth++; + break; + case PRICES_WEIGHT: // multiply by weight and consume top of stack by updating price + if ( depth == 1 ) + { + depth--; + price += pricestack[0] * ind; + den += ind; + } else errcode = -2; + break; + case PRICES_MULT: + if ( depth >= 2 ) + { + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * b) / SATOSHIDEN; + } else errcode = -3; + break; + case PRICES_DIV: + if ( depth >= 2 ) + { + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * SATOSHIDEN) / b; + } else errcode = -4; + break; + case PRICES_INV: + if ( depth >= 1 ) + { + a = pricestack[--depth]; + pricestack[depth++] = (SATOSHIDEN * SATOSHIDEN) / a; + } else errcode = -5; + break; + case PRICES_MDD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (((a * SATOSHIDEN) / b) * SATOSHIDEN) / c; + } else errcode = -6; + break; + case PRICES_MMD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * b) / c; + } else errcode = -7; + break; + case PRICES_MMM: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = ((a * b) / SATOSHIDEN) * c; + } else errcode = -8; + break; + case PRICES_DDD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (((((SATOSHIDEN * SATOSHIDEN) / a) * SATOSHIDEN) / b) * SATOSHIDEN) / c; + } else errcode = -9; + break; + default: + errcode = -10; + break; + } + if ( errcode != 0 ) + break; + } + free(pricedata); + if ( den == 0 ) + return(-11); + else if ( depth != 0 ) + return(-12); + else if ( errcode != 0 ) + return(errcode); + return(price / den); +} + +int64_t prices_syntheticprofits(int64_t &costbasis,int32_t firstheight,int32_t height,int16_t leverage,std::vector vec,int64_t positionsize,int64_t addedbets) +{ + int64_t price,profits = 0; int32_t minmax; + minmax = (height > firstheight+PRICES_DAYWINDOW); + if ( (price= prices_syntheticprice(vec,height,minmax,leverage)) < 0 ) + { + fprintf(stderr,"unexpected zero synthetic price at height.%d\n",height); + return(0); + } + if ( minmax != 0 ) + { + if ( leverage > 0 && price > costbasis ) + costbasis = price; + else if ( leverage < 0 && (costbasis == 0 || price < costbasis) ) + costbasis = price; + } + profits = ((price * SATOSHIDEN) / costbasis) - SATOSHIDEN; + profits *= leverage * positionsize; + return(positionsize + addedbets + profits); +} + +void prices_betjson(UniValue &result,int64_t profits,int64_t costbasis,int64_t positionsize,int64_t addedbets,int16_t leverage,int32_t firstheight,int64_t firstprice) +{ + result.push_back(Pair("profits",ValueFromAmount(profits))); + result.push_back(Pair("costbasis",ValueFromAmount(costbasis))); + result.push_back(Pair("positionsize",ValueFromAmount(positionsize))); + result.push_back(Pair("addedbets",ValueFromAmount(addedbets))); + result.push_back(Pair("leverage",(int64_t)leverage)); + result.push_back(Pair("firstheight",(int64_t)firstheight)); + result.push_back(Pair("firstprice",ValueFromAmount(firstprice))); +} + +int64_t prices_costbasis(CTransaction bettx) +{ + int64_t costbasis = 0; + // if vout1 is spent, follow and extract costbasis from opreturn + //uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) + + return(costbasis); +} + +int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid) +{ + int64_t addedbets = 0; + // iterate through batons, adding up vout1 -> addedbets + return(addedbets); +} + +UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; char myaddr[64]; std::string rawtx; + if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","leverage too big")); + return(result); + } + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( prices_syntheticvec(vec,synthetic) < 0 || (firstprice= prices_syntheticprice(vec,nextheight-1,1,leverage)) < 0 || vec.size() == 0 || vec.size() > 4096 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid synthetic")); + return(result); + } + if ( AddNormalinputs(mtx,mypk,amount+4*txfee,64) >= amount+4*txfee ) + { + betamount = (amount * 199) / 200; + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode,(amount-betamount)+2*txfee,pricespk)); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,betamount,pricespk,mypk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_betopret(mypk,nextheight-1,amount,leverage,firstprice,vec,zeroid)); + return(prices_rawtxresult(result,rawtx,0)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + return(result); +} + +UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) + { + if ( prices_batontxid(batontxid,bettx,bettxid) >= 0 ) + { + mtx.vin.push_back(CTxIn(batontxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_addopret(bettxid,mypk,amount)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find batonttxid")); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + return(result); +} + +UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + addedbets = prices_batontxid(batontxid,bettx,bettxid); + mtx.vin.push_back(CTxIn(bettxid,1,CScript())); + for (i=0; ievalcode,bettx.vout[1].nValue-myfee-txfee,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_costbasisopret(bettxid,mypk,firstheight+PRICES_DAYWINDOW-1,costbasis)); + return(prices_rawtxresult(result,rawtx,0)); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); +} + +UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid,batontxid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(ignore,firstheight,rektheight,leverage,vec,positionsize,addedbets)) < 0 ) + { + myfee = (positionsize + addedbets) / 500; + } + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + if ( myfee != 0 ) + { + mtx.vin.push_back(CTxIn(bettxid,2,CScript())); + mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[2].nValue-myfee-txfee,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,rektheight,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","position not rekt")); + return(result); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant decode opret")); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); +} + +UniValue PricesCashout(uint64_t txfee,uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,inputsum,ignore,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,destaddr,pricespk); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(ignore,firstheight,nextheight-1,leverage,vec,positionsize,addedbets)) < 0 ) + { + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","position rekt")); + return(result); + } + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + mtx.vin.push_back(CTxIn(bettxid,2,CScript())); + if ( (inputsum= AddPricesInputs(cp,mtx,destaddr,profits+txfee,64,bettxid,2)) > profits+txfee ) + CCchange = (inputsum - profits); + mtx.vout.push_back(CTxOut(bettx.vout[2].nValue + profits,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if ( CCchange >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,nextheight-1,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant decode opret")); + return(result); + } + } + return(result); +} + +UniValue PricesInfo(uint256 bettxid,int32_t refheight) +{ + UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(ignore,firstheight,refheight,leverage,vec,positionsize,addedbets)) < 0 ) + { + result.push_back(Pair("rekt",1)); + result.push_back(Pair("rektfee",(positionsize + addedbets) / 500)); + } else result.push_back(Pair("rekt",0)); + result.push_back(Pair("batontxid",batontxid.GetHex())); + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + result.push_back(Pair("height",(int64_t)refheight)); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); +} + UniValue PricesList() { - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; cp = CCinit(&C,EVAL_PRICES); pricespk = GetUnspendable(cp,0); - SetCCtxids(addressIndex,cp->normaladdr); + SetCCtxids(addressIndex,cp->normaladdr,false); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) { result.push_back(uint256_str(str,txid)); } @@ -176,275 +726,4 @@ UniValue PricesList() return(result); } -// longtoken satoshis limits long exposure -// shorttoken satoshis limits short exposure -// both must be in the 1of2 CC address with its total supply -// bettoken -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; - if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) - { - CCerror = "invalid parameter error"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( (N= (int32_t)pubkeys.size()) || N > 15 ) - { - fprintf(stderr,"too many pubkeys N.%d\n",N); - return(""); - } - for (i=0; i 0 ) - { - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - return(""); -} - -UniValue PricesInfo(uint256 fundingtxid) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - ERR_RESULT("cant find fundingtxid"); - return(result); - } - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); - result.push_back(Pair("bettoken",uint256_str(str,bettoken))); - result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); - sprintf(numstr,"%.3f",(double)margin/1000); - result.push_back(Pair("profitmargin",numstr)); - result.push_back(Pair("maxleverage",maxleverage)); - result.push_back(Pair("mode",(int64_t)mode)); - for (i=0; ipubkeys; - if ( amount < 10000 ) - { - CCerror = "amount must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); - if ( inputs > amount+txfee ) - CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); - // add addr2 - - std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys - return(FinalizeCCTx(0,cp,mtx,mypk,txfee, - EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty, - EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey())))); - } - else - { - CCerror = "cant find enough bet inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; - if ( amount < 0 ) - { - amount = -amount; - dir = -1; - } else dir = 1; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - if ( leverage > maxleverage || leverage < 1 ) - { - fprintf(stderr,"illegal leverage\n"); - return(""); - } - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); - if ( dir < 0 ) - tokenid = shorttoken; - else tokenid = longtoken; - exposure = leverage * amount; - longexposure = CCtoken_balance(exposureaddr,longtoken); - shortexposure = CCtoken_balance(exposureaddr,shorttoken); - netexposure = (longexposure - shortexposure + exposure*dir); - if ( netexposure < 0 ) - netexposure = -netexposure; - balance = CCtoken_balance(myaddr,bettoken) / COIN; - if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction - { - fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); - return(""); - } - if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) - { - if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); - if ( inputs > exposure+txfee ) - CCchange = (inputs - exposure); - if ( inputs2 > amount+txfee ) - CCchange2 = (inputs2 - amount); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); - // add addr2 and addr3 - //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); - CScript opret; - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - else - { - fprintf(stderr,"cant find enough bettoken inputs\n"); - return(""); - } - } - else - { - fprintf(stderr,"cant find enough exposure inputs\n"); - return(""); - } - } - else - { - CCerror = "cant find enough inputsB"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - UniValue result(UniValue::VOBJ); - // get height of bettxid - // get price and rekt - // get current height and price - // what about if rekt in the past? - return(result); -} - -std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - return(""); -} - - diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index bd5a6d919..5ca2b038a 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -203,7 +203,7 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t else { txid = tx.GetHash(); - if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 ) + if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid)) != 0 ) { if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 ) return eval->Invalid("cant find fundingtxid"); @@ -255,15 +255,23 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 ) return eval->Invalid("unexpected normal vin for unlock"); } - if ( numvouts == 2 && numvins == 1 ) + if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) + return eval->Invalid("txfee is too high"); + amount = vinTx.vout[0].nValue; + reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); + if ( reward == 0 ) + return eval->Invalid("no eligible rewards"); + if ( numvins == 1 && tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) { - if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("unlock recover tx vout.0 is not normal output"); + if ( tx.vout[1].nValue != 10000 ) + return eval->Invalid("wrong marker vout value"); + else if ( tx.vout[1].scriptPubKey != tx.vout[0].scriptPubKey ) + return eval->Invalid("unlock recover tx vout.1 mismatched scriptPubKey"); else if ( tx.vout[0].scriptPubKey != vinTx.vout[1].scriptPubKey ) return eval->Invalid("unlock recover tx vout.0 mismatched scriptPubKey"); else if ( tx.vout[0].nValue > vinTx.vout[0].nValue ) return eval->Invalid("unlock recover tx vout.0 mismatched amounts"); - else if ( tx.vout[1].nValue > 0 ) + else if ( tx.vout[2].nValue > 0 ) return eval->Invalid("unlock recover tx vout.1 nonz amount"); else return(true); } @@ -277,8 +285,6 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t return eval->Invalid("unlock tx vout.1 is CC output"); else if ( tx.vout[1].scriptPubKey != vinTx.vout[1].scriptPubKey ) return eval->Invalid("unlock tx vout.1 mismatched scriptPubKey"); - amount = vinTx.vout[0].nValue; - reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); if ( RewardsExactAmounts(cp,eval,tx,txfee+tx.vout[1].nValue,sbits,fundingtxid) == 0 ) return false; else if ( tx.vout[1].nValue > amount+reward ) @@ -310,7 +316,7 @@ static uint64_t myIs_unlockedtx_inmempool(uint256 &txid,int32_t &vout,uint64_t r if ( tx.vout.size() > 0 && tx.vout[0].nValue >= needed ) { const uint256 &hash = tx.GetHash(); - if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(hash,0) == 0 ) + if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,hash,0) == 0 ) { if ( (funcid= DecodeRewardsOpRet(hash,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'U' && sbits == refsbits && fundingtxid == reffundingtxid ) { @@ -332,8 +338,12 @@ int64_t AddRewardsInputs(CScript &scriptPubKey,uint64_t maxseconds,struct CCcont char coinaddr[64],str[65]; uint64_t threshold,sbits,nValue,totalinputs = 0; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t numblocks,j,vout,n = 0; uint8_t funcid; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); - threshold = total/(maxinputs+1); + SetCCunspents(unspentOutputs,coinaddr,true); + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -346,7 +356,7 @@ int64_t AddRewardsInputs(CScript &scriptPubKey,uint64_t maxseconds,struct CCcont break; if ( j != mtx.vin.size() ) continue; - if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( (funcid= DecodeRewardsOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) != 0 ) { @@ -393,7 +403,7 @@ int64_t RewardsPlanFunds(uint64_t &lockedfunds,uint64_t refsbits,struct CCcontra std::vector > unspentOutputs; lockedfunds = 0; GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; @@ -423,7 +433,7 @@ bool RewardsPlanExists(struct CCcontract_info *cp,uint64_t refsbits,CPubKey rewa char CCaddr[64]; uint64_t sbits; uint256 txid,hashBlock; CTransaction tx; std::vector > txids; GetCCaddress(cp,CCaddr,rewardspk); - SetCCtxids(txids,CCaddr); + SetCCtxids(txids,CCaddr,true); for (std::vector >::const_iterator it=txids.begin(); it!=txids.end(); it++) { //int height = it->first.blockHeight; @@ -483,7 +493,7 @@ UniValue RewardsList() { UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits,APR,minseconds,maxseconds,mindeposit; char str[65]; cp = CCinit(&C,EVAL_REWARDS); - SetCCtxids(addressIndex,cp->normaladdr); + SetCCtxids(addressIndex,cp->normaladdr,false); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; @@ -640,7 +650,7 @@ std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint2 else { GetCCaddress(cp,coinaddr,rewardspk); - if ( (amount= CCutxovalue(coinaddr,locktxid,0)) == 0 ) + if ( (amount= CCutxovalue(coinaddr,locktxid,0,1)) == 0 ) { fprintf(stderr,"%s locktxid/v0 is spent\n",coinaddr); CCerror = "locktxid/v0 is spent"; @@ -677,8 +687,7 @@ std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint2 } else { - firstmtx.vout.push_back(CTxOut(amount-txfee,scriptPubKey)); - //CCerror = "cant find enough rewards inputs"; + firstmtx.vout.push_back(CTxOut(amount-txfee*2,scriptPubKey)); fprintf(stderr,"not enough rewards funds to payout %.8f, recover mode tx\n",(double)(reward+txfee)/COIN); return(FinalizeCCTx(-1LL,cp,firstmtx,mypk,txfee,EncodeRewardsOpRet('U',sbits,fundingtxid))); } @@ -703,4 +712,3 @@ std::string RewardsUnlock(uint64_t txfee,char *planstr,uint256 fundingtxid,uint2 fprintf(stderr,"amount %.8f -> reward %.8f\n",(double)amount/COIN,(double)reward/COIN); return(""); } - diff --git a/src/cc/rogue/LICENSE.TXT b/src/cc/rogue/LICENSE.TXT new file mode 100644 index 000000000..ae3f4715b --- /dev/null +++ b/src/cc/rogue/LICENSE.TXT @@ -0,0 +1,93 @@ +Rogue: Exploring the Dungeons of Doom +Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +=========================================================================== + +Portions of this software (state.c, mdport.c) are based on the work +of Nicholas J. Kisseberth. Used under license: + +Copyright (C) 1999, 2000, 2005 Nicholas J. Kisseberth +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +=========================================================================== + +Portions of this software (xcrypt.c) are based on the work +of David Burren. Used under license: + +FreeSec: libcrypt + +Copyright (C) 1994 David Burren +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/src/cc/rogue/Makefile.in b/src/cc/rogue/Makefile.in new file mode 100644 index 000000000..2669a2025 --- /dev/null +++ b/src/cc/rogue/Makefile.in @@ -0,0 +1,222 @@ +############################################################################### +# +# Makefile for rogue +# +# Rogue: Exploring the Dungeons of Doom +# Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman +# All rights reserved. +# +# See the file LICENSE.TXT for full copyright and licensing information. +# +############################################################################### + +############################################################################### +# Site configuration occurs beneath this comment +# Typically ./configure (autoconf tools) configures this section +# This section could be manually configured if autoconf/configure fails +############################################################################### + +DISTNAME=@PACKAGE_TARNAME@@PACKAGE_VERSION@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@-@PACKAGE_VERSION@ +PROGRAM=@PROGRAM@ + +O=o + +#CC=gcc +CC = @CC@ + +#CFLAGS=-O2 +CFLAGS= @CFLAGS@ -fPIC + +LIBS=-lcurses -lcurl +#LIBS = @LIBS@ + +#RM=rm -f +RM = rm -f + +#GROFF=groff +GROFF = @GROFF@ + +#NROFF=nroff +NROFF = @NROFF@ + +#TBL=tbl +TBL = @TBL@ + +#COLCRT=colcrt +COLCRT = @COLCRT@ + +#SED=sed +SED = @SED@ + +#SCOREFILE=rogue54.scr +SCOREFILE = @SCOREFILE@ + +#LOCKFILE=rogue54.lck +LOCKFILE = @LOCKFILE@ + +#GROUPOWNER=games +GROUPOWNER = @GROUPOWNER@ + +#CPPFLAGS=-DHAVE_CONFIG_H +CPPFLAGS =@DEFS@ @CPPFLAGS@ + +#DISTFILE = $(PROGRAM) +DISTFILE = $(DISTNAME)-@TARGET@ + +INSTALL=./install-sh + +#INSTGROUP=-g games +INSTGROUP= +#INSTOWNER=-u root +INSTOWNER= + +CHGRP=chgrp + +MKDIR=mkdir + +TOUCH=touch + +RMDIR=rmdir + +CHMOD=chmod + +DESTDIR= + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +datarootdir=@datarootdir@ +datadir=@datadir@ +bindir=@bindir@ +mandir=@mandir@ +docdir=@docdir@ +man6dir = $(mandir)/man6 + +############################################################################### +# Site configuration occurs above this comment +# It should not be necessary to change anything below this comment +############################################################################### + +HDRS = rogue.h extern.h score.h +OBJS1 = vers.$(O) extern.$(O) armor.$(O) chase.$(O) command.$(O) \ + daemon.$(O) daemons.$(O) fight.$(O) init.$(O) io.$(O) list.$(O) \ + mach_dep.$(O) rogue.$(O) mdport.$(O) misc.$(O) monsters.$(O) \ + move.$(O) new_level.$(O) +OBJS2 = options.$(O) pack.$(O) passages.$(O) potions.$(O) rings.$(O) \ + rip.$(O) rooms.$(O) save.$(O) scrolls.$(O) state.$(O) sticks.$(O) \ + things.$(O) weapons.$(O) wizard.$(O) xcrypt.$(O) +OBJS = main.$(O) $(OBJS1) $(OBJS2) +CFILES = vers.c extern.c armor.c chase.c command.c daemon.c \ + daemons.c fight.c init.c io.c list.c mach_dep.c \ + main.c mdport.c misc.c monsters.c move.c new_level.c \ + options.c pack.c passages.c potions.c rings.c rip.c \ + rooms.c save.c scrolls.c state.c sticks.c things.c \ + weapons.c wizard.c xcrypt.c +MISC_C = findpw.c scedit.c scmisc.c +DOCSRC = rogue.me.in rogue.6.in rogue.doc.in rogue.html.in rogue.cat.in +DOCS = $(PROGRAM).doc $(PROGRAM).html $(PROGRAM).cat $(PROGRAM).me \ + $(PROGRAM).6 +AFILES = configure Makefile.in configure.ac config.h.in config.sub config.guess \ + install-sh rogue.6.in rogue.me.in rogue.html.in rogue.doc.in rogue.cat.in +MISC = Makefile.std LICENSE.TXT rogue54.sln rogue54.vcproj rogue.spec \ + rogue.png rogue.desktop + +.SUFFIXES: .obj + +.c.obj: + $(CC) $(CFLAGS) $(CPPFLAGS) /c $*.c + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $*.c + +#$(RM) rogue.so ; $(CC) -shared -o rogue.so cursesd.c $(OBJS1) $(OBJS2); $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + +$(PROGRAM): $(HDRS) $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + +clean: + $(RM) $(OBJS1); $(RM) main.$(O) ; $(RM) rogue.so + $(RM) $(OBJS2) + $(RM) core a.exe a.out a.exe.stackdump $(PROGRAM) $(PROGRAM).exe + $(RM) $(PROGRAM).tar $(PROGRAM).tar.gz $(PROGRAM).zip + $(RM) $(DISTNAME)/* + -rmdir $(DISTNAME) + +maintainer-clean: + $(RM) config.h + $(RM) Makefile + $(RM) config.status + $(RM) -r autom4te.cache + $(RM) config.log + $(RM) $(PROGRAM).scr $(PROGRAM).lck + +stddocs: + sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.6.in > rogue.6 + sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.me.in > rogue.me + sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.html.in > rogue,html + sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.doc.in > rogue.doc + sed -e 's/@PROGRAM@/rogue/' -e 's/@SCOREFILE@/rogue.scr/' rogue.cat.in > rogue.cat + +dist.src: + $(MAKE) $(MAKEFILE) clean + mkdir $(DISTNAME) + cp $(CFILES) $(HDRS) $(MISC) $(AFILES) $(DISTNAME) + tar cf $(DISTNAME)-src.tar $(DISTNAME) + gzip -f $(DISTNAME)-src.tar + rm -fr $(DISTNAME) + +findpw: findpw.c xcrypt.o mdport.o xcrypt.o + $(CC) -s -o findpw findpw.c xcrypt.o mdport.o -lcurses + +scedit: scedit.o scmisc.o vers.o mdport.o xcrypt.o + $(CC) -s -o scedit vers.o scedit.o scmisc.o mdport.o xcrypt.o -lcurses + +scmisc.o scedit.o: + $(CC) -O -c $(SF) $*.c + +$(PROGRAM).doc: rogue.me + if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \ + $(GROFF) -P-c -t -me -Tascii rogue.me | $(SED) -e 's/.\x08//g' > $(PROGRAM).doc ;\ + elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \ + tbl rogue.me | $(NROFF) -me | colcrt - > $(PROGRAM).doc ;\ + fi + +$(PROGRAM).cat: rogue.6 + if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \ + $(GROFF) -Tascii -man rogue.6 | $(SED) -e 's/.\x08//g' > $(PROGRAM).cat ;\ + elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \ + $(NROFF) -man rogue.6 | $(COLCRT) - > $(PROGRAM).cat ;\ + fi + +dist: clean $(PROGRAM) + tar cf $(DISTFILE).tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTFILE).tar + +install: $(PROGRAM) + -$(TOUCH) test + -if test ! -f $(DESTDIR)$(SCOREFILE) ; then $(INSTALL) -m 0664 test $(DESTDIR)$(SCOREFILE) ; fi + -$(INSTALL) -m 0755 $(PROGRAM) $(DESTDIR)$(bindir)/$(PROGRAM) + -if test "x$(GROUPOWNER)" != "x" ; then \ + $(CHGRP) $(GROUPOWNER) $(DESTDIR)$(SCOREFILE) ; \ + $(CHGRP) $(GROUPOWNER) $(DESTDIR)$(bindir)/$(PROGRAM) ; \ + $(CHMOD) 02755 $(DESTDIR)$(bindir)/$(PROGRAM) ; \ + $(CHMOD) 0464 $(DESTDIR)$(SCOREFILE) ; \ + fi + -if test -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(man6dir)/$(PROGRAM).6 ; fi + -if test ! -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(mandir)/$(PROGRAM).6 ; fi + -$(INSTALL) -m 0644 rogue.doc $(DESTDIR)$(docdir)/$(PROGRAM).doc + -$(INSTALL) -m 0644 rogue.html $(DESTDIR)$(docdir)/$(PROGRAM).html + -$(INSTALL) -m 0644 rogue.cat $(DESTDIR)$(docdir)/$(PROGRAM).cat + -$(INSTALL) -m 0644 LICENSE.TXT $(DESTDIR)$(docdir)/LICENSE.TXT + -$(INSTALL) -m 0644 rogue.me $(DESTDIR)$(docdir)/$(PROGRAM).me + -if test ! -f $(DESTDIR)$(LOCKFILE) ; then $(INSTALL) -m 0666 test $(DESTDIR)$(LOCKFILE) ; $(RM) $(DESTDIR)$(LOCKFILE) ; fi + -$(RM) test + +uninstall: + -$(RM) $(DESTDIR)$(bindir)/$(PROGRAM) + -$(RM) $(DESTDIR)$(man6dir)/$(PROGRAM).6 + -$(RM) $(DESTDIR)$(docdir)$(PROGRAM)/$(PROGRAM).doc + -$(RM) $(DESTDIR)$(LOCKFILE) + -$(RMDIR) $(DESTDIR)$(docdir)$(PROGRAM) + +reinstall: uninstall install diff --git a/src/cc/rogue/Makefile.std b/src/cc/rogue/Makefile.std new file mode 100755 index 000000000..923c74672 --- /dev/null +++ b/src/cc/rogue/Makefile.std @@ -0,0 +1,158 @@ +# +# Makefile for rogue +# @(#)Makefile 4.21 (Berkeley) 02/04/99 +# +# Rogue: Exploring the Dungeons of Doom +# Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman +# All rights reserved. +# +# See the file LICENSE.TXT for full copyright and licensing information. +# + +DISTNAME = rogue5.4.4 +PROGRAM = rogue54 +O = o +HDRS = rogue.h extern.h score.h +OBJS1 = vers.$(O) extern.$(O) armor.$(O) chase.$(O) command.$(O) \ + daemon.$(O) daemons.$(O) fight.$(O) init.$(O) io.$(O) list.$(O) \ + mach_dep.$(O) main.$(O) mdport.$(O) misc.$(O) monsters.$(O) \ + move.$(O) new_level.$(O) +OBJS2 = options.$(O) pack.$(O) passages.$(O) potions.$(O) rings.$(O) \ + rip.$(O) rooms.$(O) save.$(O) scrolls.$(O) state.$(O) sticks.$(O) \ + things.$(O) weapons.$(O) wizard.$(O) xcrypt.$(O) +OBJS = $(OBJS1) $(OBJS2) +CFILES = vers.c extern.c armor.c chase.c command.c daemon.c \ + daemons.c fight.c init.c io.c list.c mach_dep.c \ + main.c mdport.c misc.c monsters.c move.c new_level.c \ + options.c pack.c passages.c potions.c rings.c rip.c \ + rooms.c save.c scrolls.c state.c sticks.c things.c \ + weapons.c wizard.c xcrypt.c +MISC_C = findpw.c scedit.c scmisc.c +DOCSRC = rogue.me.in rogue.6.in rogue.doc.in rogue.html.in rogue.cat.in +DOCS = $(PROGRAM).doc $(PROGRAM).html $(PROGRAM).cat $(PROGRAM).me \ + $(PROGRAM).6 +AFILES = configure Makefile.in configure.ac config.h.in config.sub config.guess \ + install-sh rogue.6.in rogue.me.in rogue.html.in rogue.doc.in rogue.cat.in +MISC = Makefile.std LICENSE.TXT rogue54.sln rogue54.vcproj rogue.spec \ + rogue.png rogue.desktop +CC = gcc +FEATURES = -DALLSCORES -DSCOREFILE=\"$(SCOREFILE)\" -DLOCKFILE=\"$(LOCKFILE)\" +CPPFLAGS = +CFLAGS = -O3 +LDFLAGS = +LIBS = -lcurses +RM = rm -f +MAKEFILE = -f Makefile.std +SCOREFILE= $(PROGRAM).scr +LOCKFILE = $(PROGRAM).lck +OUTFLAG = -o +EXE = + +.SUFFIXES: .obj + +.c.obj: + $(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURES) /c $*.c + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) $(FEATURES) -c $*.c + +$(PROGRAM): $(HDRS) $(OBJS) fixdocs + $(CC) $(LDFLAGS) $(OBJS) $(LIBS) $(OUTFLAG)$@$(EXE) + +clean: + $(RM) $(OBJS1) + $(RM) $(OBJS2) + $(RM) core a.exe a.out a.exe.stackdump $(PROGRAM) $(PROGRAM).exe $(PROGRAM).lck + $(RM) $(PROGRAM).tar $(PROGRAM).tar.gz $(PROGRAM).zip + $(RM) $(DISTNAME)/* + +dist.src: + $(MAKE) $(MAKEFILE) clean + mkdir $(DISTNAME) + cp $(CFILES) $(HDRS) $(MISC) $(AFILES) $(DISTNAME) + tar cf $(DISTNAME)-src.tar $(DISTNAME) + gzip -f $(DISTNAME)-src.tar + rm -fr $(DISTNAME) + +findpw: findpw.c xcrypt.o mdport.o xcrypt.o + $(CC) -s -o findpw findpw.c xcrypt.o mdport.o -lcurses + +scedit: scedit.o scmisc.o vers.o mdport.o xcrypt.o + $(CC) -s -o scedit vers.o scedit.o scmisc.o mdport.o xcrypt.o -lcurses + +scmisc.o scedit.o: + $(CC) -O -c $(SF) $*.c + +doc.nroff: + tbl rogue.me | nroff -me | colcrt - > rogue.doc + nroff -man rogue.6 | colcrt - > rogue.cat + +doc.groff: + groff -P-c -t -me -Tascii rogue.me | sed -e 's/.\x08//g' > rogue.doc + groff -man rogue.6 | sed -e 's/.\x08//g' > rogue.cat + +fixdocs: + sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.6.in > $(PROGRAM).6 + sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.me.in > $(PROGRAM).me + sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.html.in > $(PROGRAM).html + sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.doc.in > $(PROGRAM).doc + sed -e 's/@PROGRAM@/$(PROGRAM)/' -e 's/@SCOREFILE@/$(SCOREFILE)/' rogue.cat.in > $(PROGRAM).cat + +dist.irix: + $(MAKE) $(MAKEFILE) clean + $(MAKE) $(MAKEFILE) CC=cc $(PROGRAM) + tar cf $(DISTNAME)-irix.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-irix.tar + +dist.aix: + $(MAKE) $(MAKEFILE) clean + $(MAKE) $(MAKEFILE) CC=xlc CFLAGS="-qmaxmem=16768 -O3 -qstrict" $(PROGRAM) + tar cf $(DISTNAME)-aix.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-aix.tar + +dist.linux: + $(MAKE) $(MAKEFILE) clean + $(MAKE) $(MAKEFILE) $(PROGRAM) + tar cf $(DISTNAME)-linux.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-linux.tar + +dist.interix: + @$(MAKE) $(MAKEFILE) clean + @$(MAKE) $(MAKEFILE) CFLAGS="-ansi" $(PROGRAM) + tar cf $(DISTNAME)-interix.tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-interix.tar + +dist.cygwin: + @$(MAKE) $(MAKEFILE) --no-print-directory clean + @$(MAKE) $(MAKEFILE) CPPFLAGS="-I/usr/include/ncurses" --no-print-directory $(PROGRAM) + tar cf $(DISTNAME)-cygwin.tar $(PROGRAM).exe LICENSE.TXT $(DOCS) + gzip -f $(DISTNAME)-cygwin.tar + +# +# Use MINGW32-MAKE to build this target +# +dist.mingw32: + @$(MAKE) $(MAKEFILE) --no-print-directory RM="cmd /c del" clean + @$(MAKE) $(MAKEFILE) --no-print-directory CPPFLAGS="-I../pdcurses" LIBS="../pdcurses/pdcurses.a" $(PROGRAM) + cmd /c del $(DISTNAME)-mingw32.zip + zip $(DISTNAME)-mingw32.zip $(PROGRAM).exe LICENSE.TXT $(DOCS) + +dist.djgpp: + @$(MAKE) $(MAKEFILE) --no-print-directory clean + @$(MAKE) $(MAKEFILE) --no-print-directory LDFLAGS="-L$(DJDIR)/LIB" \ + LIBS="-lpdcurses" $(PROGRAM) + rm -f $(DISTNAME)-djgpp.zip + zip $(DISTNAME)-djgpp.zip $(PROGRAM) LICENSE.TXT $(DOCS) + +# +# Use NMAKE to build this targer +# + +dist.win32: + @$(MAKE) $(MAKEFILE) /NOLOGO O="obj" RM="-del" clean + @$(MAKE) $(MAKEFILE) /NOLOGO O="obj" CC="CL" \ + LIBS="..\pdcurses\pdcurses.lib shell32.lib user32.lib Advapi32.lib" \ + EXE=".exe" OUTFLAG="/Fe" CPPFLAGS="-I..\pdcurses" \ + CFLAGS="-nologo -Ox -wd4033 -wd4716" $(PROGRAM) + -del $(DISTNAME)-win32.zip + zip $(DISTNAME)-win32.zip $(PROGRAM).exe LICENSE.TXT $(DOCS) diff --git a/src/cc/rogue/Makefile_win b/src/cc/rogue/Makefile_win new file mode 100644 index 000000000..e5a36d9b4 --- /dev/null +++ b/src/cc/rogue/Makefile_win @@ -0,0 +1,222 @@ +############################################################################### +# +# Makefile for rogue +# +# Rogue: Exploring the Dungeons of Doom +# Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman +# All rights reserved. +# +# See the file LICENSE.TXT for full copyright and licensing information. +# +############################################################################### + +############################################################################### +# Site configuration occurs beneath this comment +# Typically ./configure (autoconf tools) configures this section +# This section could be manually configured if autoconf/configure fails +############################################################################### + +DISTNAME=rogue5.4.4 +PACKAGE_TARNAME = rogue-5.4.4 +PROGRAM=rogue + +O=o + +#CC=gcc +CC = x86_64-w64-mingw32-gcc + +#CFLAGS=-O2 +CFLAGS= -g -O2 -I./x86_64-w64-mingw32/include -I./x86_64-w64-mingw32/include/ncursesw + +#LIBS=-lcurses +LIBS = -L./x86_64-w64-mingw32/lib -lncursesw -lcurl + +#RM=rm -f +RM = rm -f + +#GROFF=groff +GROFF = groff + +#NROFF=nroff +NROFF = nroff + +#TBL=tbl +TBL = tbl + +#COLCRT=colcrt +COLCRT = colcrt + +#SED=sed +SED = sed + +#SCOREFILE=rogue54.scr +SCOREFILE = rogue.scr + +#LOCKFILE=rogue54.lck +LOCKFILE = rogue.lck + +#GROUPOWNER=games +GROUPOWNER = + +#CPPFLAGS=-DHAVE_CONFIG_H +CPPFLAGS =-DHAVE_CONFIG_H + +#DISTFILE = $(PROGRAM) +DISTFILE = $(DISTNAME)-x86_64-w64-mingw32 + +INSTALL=./install-sh + +#INSTGROUP=-g games +INSTGROUP= +#INSTOWNER=-u root +INSTOWNER= + +CHGRP=chgrp + +MKDIR=mkdir + +TOUCH=touch + +RMDIR=rmdir + +CHMOD=chmod + +DESTDIR= + +prefix=/usr/local +exec_prefix=${prefix} +datarootdir=${prefix}/share +datadir=${datarootdir} +bindir=${exec_prefix}/bin +mandir=${datarootdir}/man +docdir=${datarootdir}/doc/${PACKAGE_TARNAME} +man6dir = $(mandir)/man6 + +############################################################################### +# Site configuration occurs above this comment +# It should not be necessary to change anything below this comment +############################################################################### + +HDRS = rogue.h extern.h score.h +OBJS1 = vers.$(O) extern.$(O) armor.$(O) chase.$(O) command.$(O) \ + daemon.$(O) daemons.$(O) fight.$(O) init.$(O) io.$(O) list.$(O) \ + mach_dep.$(O) rogue.$(O) mdport.$(O) misc.$(O) monsters.$(O) \ + move.$(O) new_level.$(O) +OBJS2 = options.$(O) pack.$(O) passages.$(O) potions.$(O) rings.$(O) \ + rip.$(O) rooms.$(O) save.$(O) scrolls.$(O) state.$(O) sticks.$(O) \ + things.$(O) weapons.$(O) wizard.$(O) xcrypt.$(O) +OBJS = main.$(O) $(OBJS1) $(OBJS2) +CFILES = vers.c extern.c armor.c chase.c command.c daemon.c \ + daemons.c fight.c init.c io.c list.c mach_dep.c \ + main.c mdport.c misc.c monsters.c move.c new_level.c \ + options.c pack.c passages.c potions.c rings.c rip.c \ + rooms.c save.c scrolls.c state.c sticks.c things.c \ + weapons.c wizard.c xcrypt.c +MISC_C = findpw.c scedit.c scmisc.c +DOCSRC = rogue.me.in rogue.6.in rogue.doc.in rogue.html.in rogue.cat.in +DOCS = $(PROGRAM).doc $(PROGRAM).html $(PROGRAM).cat $(PROGRAM).me \ + $(PROGRAM).6 +AFILES = configure Makefile.in configure.ac config.h.in config.sub config.guess \ + install-sh rogue.6.in rogue.me.in rogue.html.in rogue.doc.in rogue.cat.in +MISC = Makefile.std LICENSE.TXT rogue54.sln rogue54.vcproj rogue.spec \ + rogue.png rogue.desktop + +.SUFFIXES: .obj + +.c.obj: + $(CC) $(CFLAGS) $(CPPFLAGS) /c $*.c + +.c.o: + $(CC) $(CFLAGS) $(CPPFLAGS) -c $*.c + +#$(RM) rogue.so ; $(CC) -shared -o rogue.so cursesd.c $(OBJS1) $(OBJS2); $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@ + +$(PROGRAM): $(HDRS) $(OBJS) + $(CC) $(CFLAGS) $(LDFLAGS) $(OBJS) $(LIBS) -o $@.exe + +clean: + $(RM) $(OBJS1); $(RM) main.$(O) ; $(RM) rogue.so + $(RM) $(OBJS2) + $(RM) core a.exe a.out a.exe.stackdump $(PROGRAM) $(PROGRAM).exe + $(RM) $(PROGRAM).tar $(PROGRAM).tar.gz $(PROGRAM).zip + $(RM) $(DISTNAME)/* + -rmdir $(DISTNAME) + +maintainer-clean: + $(RM) config.h + $(RM) Makefile + $(RM) config.status + $(RM) -r autom4te.cache + $(RM) config.log + $(RM) $(PROGRAM).scr $(PROGRAM).lck + +stddocs: + sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.6.in > rogue.6 + sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.me.in > rogue.me + sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.html.in > rogue,html + sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.doc.in > rogue.doc + sed -e 's/rogue/rogue/' -e 's/rogue.scr/rogue.scr/' rogue.cat.in > rogue.cat + +dist.src: + $(MAKE) $(MAKEFILE) clean + mkdir $(DISTNAME) + cp $(CFILES) $(HDRS) $(MISC) $(AFILES) $(DISTNAME) + tar cf $(DISTNAME)-src.tar $(DISTNAME) + gzip -f $(DISTNAME)-src.tar + rm -fr $(DISTNAME) + +findpw: findpw.c xcrypt.o mdport.o xcrypt.o + $(CC) -s -o findpw findpw.c xcrypt.o mdport.o -lcurses + +scedit: scedit.o scmisc.o vers.o mdport.o xcrypt.o + $(CC) -s -o scedit vers.o scedit.o scmisc.o mdport.o xcrypt.o -lcurses + +scmisc.o scedit.o: + $(CC) -O -c $(SF) $*.c + +$(PROGRAM).doc: rogue.me + if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \ + $(GROFF) -P-c -t -me -Tascii rogue.me | $(SED) -e 's/.\x08//g' > $(PROGRAM).doc ;\ + elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \ + tbl rogue.me | $(NROFF) -me | colcrt - > $(PROGRAM).doc ;\ + fi + +$(PROGRAM).cat: rogue.6 + if test "x$(GROFF)" != "x" -a "x$(SED)" != "x" ; then \ + $(GROFF) -Tascii -man rogue.6 | $(SED) -e 's/.\x08//g' > $(PROGRAM).cat ;\ + elif test "x$(NROFF)" != "x" -a "x$(TBL)" != "x" -a "x$(COLCRT)" != "x" ; then \ + $(NROFF) -man rogue.6 | $(COLCRT) - > $(PROGRAM).cat ;\ + fi + +dist: clean $(PROGRAM) + tar cf $(DISTFILE).tar $(PROGRAM) LICENSE.TXT $(DOCS) + gzip -f $(DISTFILE).tar + +install: $(PROGRAM) + -$(TOUCH) test + -if test ! -f $(DESTDIR)$(SCOREFILE) ; then $(INSTALL) -m 0664 test $(DESTDIR)$(SCOREFILE) ; fi + -$(INSTALL) -m 0755 $(PROGRAM) $(DESTDIR)$(bindir)/$(PROGRAM) + -if test "x$(GROUPOWNER)" != "x" ; then \ + $(CHGRP) $(GROUPOWNER) $(DESTDIR)$(SCOREFILE) ; \ + $(CHGRP) $(GROUPOWNER) $(DESTDIR)$(bindir)/$(PROGRAM) ; \ + $(CHMOD) 02755 $(DESTDIR)$(bindir)/$(PROGRAM) ; \ + $(CHMOD) 0464 $(DESTDIR)$(SCOREFILE) ; \ + fi + -if test -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(man6dir)/$(PROGRAM).6 ; fi + -if test ! -d $(man6dir) ; then $(INSTALL) -m 0644 rogue.6 $(DESTDIR)$(mandir)/$(PROGRAM).6 ; fi + -$(INSTALL) -m 0644 rogue.doc $(DESTDIR)$(docdir)/$(PROGRAM).doc + -$(INSTALL) -m 0644 rogue.html $(DESTDIR)$(docdir)/$(PROGRAM).html + -$(INSTALL) -m 0644 rogue.cat $(DESTDIR)$(docdir)/$(PROGRAM).cat + -$(INSTALL) -m 0644 LICENSE.TXT $(DESTDIR)$(docdir)/LICENSE.TXT + -$(INSTALL) -m 0644 rogue.me $(DESTDIR)$(docdir)/$(PROGRAM).me + -if test ! -f $(DESTDIR)$(LOCKFILE) ; then $(INSTALL) -m 0666 test $(DESTDIR)$(LOCKFILE) ; $(RM) $(DESTDIR)$(LOCKFILE) ; fi + -$(RM) test + +uninstall: + -$(RM) $(DESTDIR)$(bindir)/$(PROGRAM) + -$(RM) $(DESTDIR)$(man6dir)/$(PROGRAM).6 + -$(RM) $(DESTDIR)$(docdir)$(PROGRAM)/$(PROGRAM).doc + -$(RM) $(DESTDIR)$(LOCKFILE) + -$(RMDIR) $(DESTDIR)$(docdir)$(PROGRAM) + +reinstall: uninstall install diff --git a/src/cc/rogue/armor.c b/src/cc/rogue/armor.c new file mode 100644 index 000000000..20c5704a8 --- /dev/null +++ b/src/cc/rogue/armor.c @@ -0,0 +1,89 @@ +/* + * This file contains misc functions for dealing with armor + * @(#)armor.c 4.14 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +#include "rogue.h" + +/* + * wear: + * The player wants to wear something, so let him/her put it on. + */ +void +wear(struct rogue_state *rs) +{ + register THING *obj; + register char *sp; + + if ((obj = get_item(rs,"wear", ARMOR)) == NULL) + return; + if (cur_armor != NULL) + { + addmsg(rs,"you are already wearing some"); + if (!terse) + addmsg(rs,". You'll have to take it off first"); + endmsg(rs); + after = FALSE; + return; + } + if (obj->o_type != ARMOR) + { + msg(rs,"you can't wear that"); + return; + } + waste_time(rs); + obj->o_flags |= ISKNOW; + sp = inv_name(obj, TRUE); + cur_armor = obj; + if (!terse) + addmsg(rs,"you are now "); + msg(rs,"wearing %s", sp); +} + +/* + * take_off: + * Get the armor off of the players back + */ +void +take_off(struct rogue_state *rs) +{ + register THING *obj; + + if ((obj = cur_armor) == NULL) + { + after = FALSE; + if (terse) + msg(rs,"not wearing armor"); + else + msg(rs,"you aren't wearing any armor"); + return; + } + if (!dropcheck(rs,cur_armor)) + return; + cur_armor = NULL; + if (terse) + addmsg(rs,"was"); + else + addmsg(rs,"you used to be"); + msg(rs," wearing %c) %s", obj->o_packch, inv_name(obj, TRUE)); +} + +/* + * waste_time: + * Do nothing but let other things happen + */ +void +waste_time(struct rogue_state *rs) +{ + do_daemons(rs,BEFORE); + do_fuses(rs,BEFORE); + do_daemons(rs,AFTER); + do_fuses(rs,AFTER); +} diff --git a/src/cc/rogue/chase.c b/src/cc/rogue/chase.c new file mode 100644 index 000000000..059df470d --- /dev/null +++ b/src/cc/rogue/chase.c @@ -0,0 +1,541 @@ +/* + * Code for one creature to chase another + * + * @(#)chase.c 4.57 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" + +#define DRAGONSHOT 5 /* one chance in DRAGONSHOT that a dragon will flame */ + +static coord ch_ret; /* Where chasing takes you */ + +/* + * runners: + * Make all the running monsters move. + */ +void +runners(struct rogue_state *rs,int arg) +{ + register THING *tp; + THING *next; + bool wastarget; + static coord orig_pos; + + for (tp = mlist; tp != NULL; tp = next) + { + /* remember this in case the monster's "next" is changed */ + next = next(tp); + if (!on(*tp, ISHELD) && on(*tp, ISRUN)) + { + orig_pos = tp->t_pos; + wastarget = on(*tp, ISTARGET); + if (move_monst(rs,tp) == -1) + continue; + if (on(*tp, ISFLY) && dist_cp(&hero, &tp->t_pos) >= 3) + move_monst(rs,tp); + if (wastarget && !ce(orig_pos, tp->t_pos)) + { + tp->t_flags &= ~ISTARGET; + to_death = FALSE; + } + } + } + if (has_hit) + { + endmsg(rs); + has_hit = FALSE; + } +} + +/* + * move_monst: + * Execute a single turn of running for a monster + */ +int +move_monst(struct rogue_state *rs,THING *tp) +{ + if (!on(*tp, ISSLOW) || tp->t_turn) + if (do_chase(rs,tp) == -1) + return(-1); + if (on(*tp, ISHASTE)) + if (do_chase(rs,tp) == -1) + return(-1); + tp->t_turn ^= TRUE; + return(0); +} + +/* + * relocate: + * Make the monster's new location be the specified one, updating + * all the relevant state. + */ +void +relocate(struct rogue_state *rs,THING *th, coord *new_loc) +{ + struct room *oroom; + + if (!ce(*new_loc, th->t_pos)) + { + mvaddch(th->t_pos.y, th->t_pos.x, th->t_oldch); + th->t_room = roomin(rs,new_loc); + set_oldch(th, new_loc); + oroom = th->t_room; + moat(th->t_pos.y, th->t_pos.x) = NULL; + + if (oroom != th->t_room) + th->t_dest = find_dest(rs,th); + th->t_pos = *new_loc; + moat(new_loc->y, new_loc->x) = th; + } + move(new_loc->y, new_loc->x); + if (see_monst(th)) + addch(th->t_disguise); + else if (on(player, SEEMONST)) + { + standout(); + addch(th->t_type); + standend(); + } +} + +/* + * do_chase: + * Make one thing chase another. + */ +int +do_chase(struct rogue_state *rs,THING *th) +{ + register coord *cp; + register struct room *rer, *ree; /* room of chaser, room of chasee */ + register int mindist = 32767, curdist; + register bool stoprun = FALSE; /* TRUE means we are there */ + register bool door; + register THING *obj; + static coord DEST; /* Temporary destination for chaser */ + + rer = th->t_room; /* Find room of chaser */ + if (on(*th, ISGREED) && rer->r_goldval == 0) + th->t_dest = &hero; /* If gold has been taken, run after hero */ + if (th->t_dest == &hero) /* Find room of chasee */ + ree = proom; + else + ree = roomin(rs,th->t_dest); + /* + * We don't count doors as inside rooms for this routine + */ + door = (chat(th->t_pos.y, th->t_pos.x) == DOOR); + /* + * If the object of our desire is in a different room, + * and we are not in a corridor, run to the door nearest to + * our goal. + */ +over: + if (rer != ree) + { + for (cp = rer->r_exit; cp < &rer->r_exit[rer->r_nexits]; cp++) + { + curdist = dist_cp(th->t_dest, cp); + if (curdist < mindist) + { + DEST = *cp; + mindist = curdist; + } + } + if (door) + { + rer = &passages[flat(th->t_pos.y, th->t_pos.x) & F_PNUM]; + door = FALSE; + goto over; + } + } + else + { + DEST = *th->t_dest; + /* + * For dragons check and see if (a) the hero is on a straight + * line from it, and (b) that it is within shooting distance, + * but outside of striking range. + */ + if (th->t_type == 'D' && (th->t_pos.y == hero.y || th->t_pos.x == hero.x + || abs(th->t_pos.y - hero.y) == abs(th->t_pos.x - hero.x)) + && dist_cp(&th->t_pos, &hero) <= BOLT_LENGTH * BOLT_LENGTH + && !on(*th, ISCANC) && rnd(DRAGONSHOT) == 0) + { + delta.y = sign(hero.y - th->t_pos.y); + delta.x = sign(hero.x - th->t_pos.x); + if (has_hit) + endmsg(rs); + fire_bolt(rs,&th->t_pos, &delta, "flame"); + running = FALSE; + count = 0; + quiet = 0; + if (to_death && !on(*th, ISTARGET)) + { + to_death = FALSE; + kamikaze = FALSE; + } + return(0); + } + } + /* + * This now contains what we want to run to this time + * so we run to it. If we hit it we either want to fight it + * or stop running + */ + if (!chase(th, &DEST)) + { + if (ce(DEST, hero)) + { + return( attack(rs,th) ); + } + else if (ce(DEST, *th->t_dest)) + { + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + if (th->t_dest == &obj->o_pos) + { + detach(lvl_obj, obj); + attach(th->t_pack, obj); + chat(obj->o_pos.y, obj->o_pos.x) = + (th->t_room->r_flags & ISGONE) ? PASSAGE : FLOOR; + th->t_dest = find_dest(rs,th); + break; + } + if (th->t_type != 'F') + stoprun = TRUE; + } + } + else + { + if (th->t_type == 'F') + return(0); + } + relocate(rs,th, &ch_ret); + /* + * And stop running if need be + */ + if (stoprun && ce(th->t_pos, *(th->t_dest))) + th->t_flags &= ~ISRUN; + return(0); +} + +/* + * set_oldch: + * Set the oldch character for the monster + */ +void +set_oldch(THING *tp, coord *cp) +{ + char sch; + + if (ce(tp->t_pos, *cp)) + return; + + sch = tp->t_oldch; + tp->t_oldch = CCHAR( mvinch(cp->y,cp->x) ); + if (!on(player, ISBLIND)) + { + if ((sch == FLOOR || tp->t_oldch == FLOOR) && + (tp->t_room->r_flags & ISDARK)) + tp->t_oldch = ' '; + else if (dist_cp(cp, &hero) <= LAMPDIST && see_floor) + tp->t_oldch = chat(cp->y, cp->x); + } +} + +/* + * see_monst: + * Return TRUE if the hero can see the monster + */ +bool +see_monst(THING *mp) +{ + int y, x; + + if (on(player, ISBLIND)) + return FALSE; + if (on(*mp, ISINVIS) && !on(player, CANSEE)) + return FALSE; + y = mp->t_pos.y; + x = mp->t_pos.x; + if (dist(y, x, hero.y, hero.x) < LAMPDIST) + { + if (y != hero.y && x != hero.x && + !step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x))) + return FALSE; + return TRUE; + } + if (mp->t_room != proom) + return FALSE; + return ((bool)!(mp->t_room->r_flags & ISDARK)); +} + +/* + * runto: + * Set a monster running after the hero. + */ +void +runto(struct rogue_state *rs,coord *runner) +{ + register THING *tp; + + /* + * If we couldn't find him, something is funny + */ +#ifdef MASTER + if ((tp = moat(runner->y, runner->x)) == NULL) + msg(rs,"couldn't find monster in runto at (%d,%d)", runner->y, runner->x); +#else + tp = moat(runner->y, runner->x); +#endif + /* + * Start the beastie running + */ + tp->t_flags |= ISRUN; + tp->t_flags &= ~ISHELD; + tp->t_dest = find_dest(rs,tp); +} + +/* + * chase: + * Find the spot for the chaser(er) to move closer to the + * chasee(ee). Returns TRUE if we want to keep on chasing later + * FALSE if we reach the goal. + */ +bool +chase(THING *tp, coord *ee) +{ + register THING *obj; + register int x, y; + register int curdist, thisdist; + register coord *er = &tp->t_pos; + register char ch; + register int plcnt = 1; + static coord tryp; + + /* + * If the thing is confused, let it move randomly. Invisible + * Stalkers are slightly confused all of the time, and bats are + * quite confused all the time + */ + if ((on(*tp, ISHUH) && rnd(5) != 0) || (tp->t_type == 'P' && rnd(5) == 0) + || (tp->t_type == 'B' && rnd(2) == 0)) + { + /* + * get a valid random move + */ + ch_ret = *rndmove(tp); + curdist = dist_cp(&ch_ret, ee); + /* + * Small chance that it will become un-confused + */ + if (rnd(20) == 0) + tp->t_flags &= ~ISHUH; + } + /* + * Otherwise, find the empty spot next to the chaser that is + * closest to the chasee. + */ + else + { + register int ey, ex; + /* + * This will eventually hold where we move to get closer + * If we can't find an empty spot, we stay where we are. + */ + curdist = dist_cp(er, ee); + ch_ret = *er; + + ey = er->y + 1; + if (ey >= NUMLINES - 1) + ey = NUMLINES - 2; + ex = er->x + 1; + if (ex >= NUMCOLS) + ex = NUMCOLS - 1; + + for (x = er->x - 1; x <= ex; x++) + { + if (x < 0) + continue; + tryp.x = x; + for (y = er->y - 1; y <= ey; y++) + { + tryp.y = y; + if (!diag_ok(er, &tryp)) + continue; + ch = winat(y, x); + if (step_ok(ch)) + { + /* + * If it is a scroll, it might be a scare monster scroll + * so we need to look it up to see what type it is. + */ + if (ch == SCROLL) + { + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + { + if (y == obj->o_pos.y && x == obj->o_pos.x) + break; + } + if (obj != NULL && obj->o_which == S_SCARE) + continue; + } + /* + * It can also be a Xeroc, which we shouldn't step on + */ + if ((obj = moat(y, x)) != NULL && obj->t_type == 'X') + continue; + /* + * If we didn't find any scrolls at this place or it + * wasn't a scare scroll, then this place counts + */ + thisdist = dist(y, x, ee->y, ee->x); + if (thisdist < curdist) + { + plcnt = 1; + ch_ret = tryp; + curdist = thisdist; + } + else if (thisdist == curdist && rnd(++plcnt) == 0) + { + ch_ret = tryp; + curdist = thisdist; + } + } + } + } + } + return (bool)(curdist != 0 && !ce(ch_ret, hero)); +} + +/* + * roomin: + * Find what room some coordinates are in. NULL means they aren't + * in any room. + */ +struct room * +roomin(struct rogue_state *rs,coord *cp) +{ + register struct room *rp; + register char *fp; + + + fp = &flat(cp->y, cp->x); + if (*fp & F_PASS) + return &passages[*fp & F_PNUM]; + + for (rp = rooms; rp < &rooms[MAXROOMS]; rp++) + if (cp->x <= rp->r_pos.x + rp->r_max.x && rp->r_pos.x <= cp->x + && cp->y <= rp->r_pos.y + rp->r_max.y && rp->r_pos.y <= cp->y) + return rp; + + msg(rs,"in some bizarre place (%d, %d)", unc(*cp)); +#ifdef MASTER + abort(); + return NULL; +#else + return NULL; +#endif +} + +/* + * diag_ok: + * Check to see if the move is legal if it is diagonal + */ +bool +diag_ok(coord *sp, coord *ep) +{ + if (ep->x < 0 || ep->x >= NUMCOLS || ep->y <= 0 || ep->y >= NUMLINES - 1) + return FALSE; + if (ep->x == sp->x || ep->y == sp->y) + return TRUE; + return (bool)(step_ok(chat(ep->y, sp->x)) && step_ok(chat(sp->y, ep->x))); +} + +/* + * cansee: + * Returns true if the hero can see a certain coordinate. + */ +bool +cansee(struct rogue_state *rs,int y, int x) +{ + register struct room *rer; + static coord tp; + + if (on(player, ISBLIND)) + return FALSE; + if (dist(y, x, hero.y, hero.x) < LAMPDIST) + { + if (flat(y, x) & F_PASS) + if (y != hero.y && x != hero.x && + !step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x))) + return FALSE; + return TRUE; + } + /* + * We can only see if the hero in the same room as + * the coordinate and the room is lit or if it is close. + */ + tp.y = y; + tp.x = x; + return (bool)((rer = roomin(rs,&tp)) == proom && !(rer->r_flags & ISDARK)); +} + +/* + * find_dest: + * find the proper destination for the monster + */ +coord * +find_dest(struct rogue_state *rs,THING *tp) +{ + register THING *obj; + register int prob; + + if ((prob = monsters[tp->t_type - 'A'].m_carry) <= 0 || tp->t_room == proom + || see_monst(tp)) + return &hero; + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + { + if (obj->o_type == SCROLL && obj->o_which == S_SCARE) + continue; + if (roomin(rs,&obj->o_pos) == tp->t_room && rnd(100) < prob) + { + for (tp = mlist; tp != NULL; tp = next(tp)) + if (tp->t_dest == &obj->o_pos) + break; + if (tp == NULL) + return &obj->o_pos; + } + } + return &hero; +} + +/* + * dist: + * Calculate the "distance" between to points. Actually, + * this calculates d^2, not d, but that's good enough for + * our purposes, since it's only used comparitively. + */ +int +dist(int y1, int x1, int y2, int x2) +{ + return ((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); +} + +/* + * dist_cp: + * Call dist() with appropriate arguments for coord pointers + */ +int +dist_cp(coord *c1, coord *c2) +{ + return dist(c1->y, c1->x, c2->y, c2->x); +} diff --git a/src/cc/rogue/command.c b/src/cc/rogue/command.c new file mode 100644 index 000000000..568f4b8f8 --- /dev/null +++ b/src/cc/rogue/command.c @@ -0,0 +1,844 @@ +/* + * Read and execute the user commands + * + * @(#)command.c 4.73 (Berkeley) 08/06/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +#include "rogue.h" + +/* + * command: + * Process the user commands + */ +void +command(struct rogue_state *rs) +{ + register char ch; + register int ntimes = 1; /* Number of player moves */ + char *fp; + THING *mp; + static char countch, direction, newcount = FALSE; + if (on(player, ISHASTE)) + ntimes++; + /* + * Let the daemons start up + */ + do_daemons(rs,BEFORE); + do_fuses(rs,BEFORE); + while (ntimes--) + { + if ( rs->replaydone != 0 ) + return; + again = FALSE; + if (has_hit) + { + endmsg(rs); + has_hit = FALSE; + } + /* + * these are illegal things for the player to be, so if any are + * set, someone's been poking in memeory + */ + if (on(player, ISSLOW|ISGREED|ISINVIS|ISREGEN|ISTARGET)) + exit(1); + + look(rs,TRUE); + if (!running) + door_stop = FALSE; + status(rs); + lastscore = purse; + move(hero.y, hero.x); + if ( rs->sleeptime != 0 ) + { + if (!((running || count) && jump)) + refresh(); /* Draw screen */ + } + take = 0; + after = TRUE; + /* + * Read command or continue run + */ +#ifdef MASTER + if (wizard) + noscore = TRUE; +#endif + if (!no_command) + { + if (running || to_death) + ch = runch; + else if (count) + ch = countch; + else + { + ch = readchar(rs); + move_on = FALSE; + if (mpos != 0) /* Erase message if its there */ + msg(rs,""); + } + } + else + ch = '.'; + if (no_command) + { + if (--no_command == 0) + { + player.t_flags |= ISRUN; + msg(rs,"you can move again"); + } + } + else + { + /* + * check for prefixes + */ + newcount = FALSE; + if (isdigit(ch)) + { + count = 0; + newcount = TRUE; + while (isdigit(ch)) + { + count = count * 10 + (ch - '0'); + if (count > 255) + count = 255; + ch = readchar(rs); + } + countch = ch; + /* + * turn off count for commands which don't make sense + * to repeat + */ + if ( rs->guiflag == 0 && rs->replaydone != 0 ) + ch = 'Q'; + switch (ch) + { + case CTRL('B'): case CTRL('H'): case CTRL('J'): + case CTRL('K'): case CTRL('L'): case CTRL('N'): + case CTRL('U'): case CTRL('Y'): + case '.': case 'a': case 'b': case 'h': case 'j': + case 'k': case 'l': case 'm': case 'n': case 'q': + case 'r': case 's': case 't': case 'u': case 'y': + case 'z': case 'B': case 'C': case 'H': case 'I': + case 'J': case 'K': case 'L': case 'N': case 'U': + case 'Y': +#ifdef MASTER + case CTRL('D'): case CTRL('A'): +#endif + break; + default: + count = 0; + } + } + /* + * execute a command + */ + if (count && !running) + count--; + if (ch != 'a' && ch != ESCAPE && !(running || count || to_death)) + { + l_last_comm = last_comm; + l_last_dir = last_dir; + l_last_pick = last_pick; + last_comm = ch; + last_dir = '\0'; + last_pick = NULL; + } +over: + switch (ch) + { + case ',': { + THING *obj = NULL; + int found = 0; + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + { + if (obj->o_pos.y == hero.y && obj->o_pos.x == hero.x) + { + found=1; + break; + } + } + + if (found) { + if (levit_check(rs)) + ; + else + pick_up(rs,(char)obj->o_type); + } + else { + if (!terse) + addmsg(rs,"there is "); + addmsg(rs,"nothing here"); + if (!terse) + addmsg(rs," to pick up"); + endmsg(rs); + } + } + when '!': shell(rs); + when 'h': do_move(rs,0, -1); + when 'j': do_move(rs,1, 0); + when 'k': do_move(rs,-1, 0); + when 'l': do_move(rs,0, 1); + when 'y': do_move(rs,-1, -1); + when 'u': do_move(rs,-1, 1); + when 'b': do_move(rs,1, -1); + when 'n': do_move(rs,1, 1); + when 'H': do_run('h'); + when 'J': do_run('j'); + when 'K': do_run('k'); + when 'L': do_run('l'); + when 'Y': do_run('y'); + when 'U': do_run('u'); + when 'B': do_run('b'); + when 'N': do_run('n'); + when CTRL('H'): case CTRL('J'): case CTRL('K'): case CTRL('L'): + case CTRL('Y'): case CTRL('U'): case CTRL('B'): case CTRL('N'): + { + if (!on(player, ISBLIND)) + { + door_stop = TRUE; + firstmove = TRUE; + } + if (count && !newcount) + ch = direction; + else + { + ch += ('A' - CTRL('A')); + direction = ch; + } + goto over; + } + when 'F': + kamikaze = TRUE; + /* FALLTHROUGH */ + case 'f': + if (!get_dir(rs)) + { + after = FALSE; + break; + } + delta.y += hero.y; + delta.x += hero.x; + if ( ((mp = moat(delta.y, delta.x)) == NULL) + || ((!see_monst(mp)) && !on(player, SEEMONST))) + { + if (!terse) + addmsg(rs,"I see "); + msg(rs,"no monster there"); + after = FALSE; + } + else if (diag_ok(&hero, &delta)) + { + to_death = TRUE; + max_hit = 0; + mp->t_flags |= ISTARGET; + runch = ch = dir_ch; + goto over; + } + when 't': + if (!get_dir(rs)) + after = FALSE; + else + missile(rs,delta.y, delta.x); + when 'a': + if (last_comm == '\0') + { + msg(rs,"you haven't typed a command yet"); + after = FALSE; + } + else + { + ch = last_comm; + again = TRUE; + goto over; + } + case 'q': quaff(rs); + break; + when 'Q': + after = FALSE; + q_comm = TRUE; + if ( _quit() > 0 ) + { + if ( rs->guiflag != 0 ) + { + if (rs->needflush == 0 ) + rs->needflush = (uint32_t)time(NULL); + rogue_bailout(rs); + } else rs->replaydone = (uint32_t)time(NULL); + } + q_comm = FALSE; + return; + when 'i': after = FALSE; inventory(rs,pack, 0); + when 'I': after = FALSE; picky_inven(rs); + when 'd': drop(rs); + when 'r': read_scroll(rs); + when 'e': eat(rs); + when 'w': wield(rs); + when 'W': wear(rs); + when 'T': take_off(rs); + when 'P': ring_on(rs); + when 'R': ring_off(rs); + when 'o': option(rs); after = FALSE; + when 'c': call(rs); after = FALSE; + + when '>': after = FALSE; d_level(rs); + if ( rs->guiflag != 0 && rs->needflush == 0 ) + rs->needflush = (uint32_t)time(NULL); + + when '<': after = FALSE; u_level(rs); + if ( rs->guiflag != 0 && rs->needflush == 0 ) + rs->needflush = (uint32_t)time(NULL); + + when '?': after = FALSE; help(rs); + when '/': after = FALSE; identify(rs); + when 's': search(rs); + when 'z': + if (get_dir(rs)) + do_zap(rs); + else + after = FALSE; + when 'D': after = FALSE; discovered(rs); + when CTRL('P'): after = FALSE; msg(rs,huh); + when CTRL('R'): + after = FALSE; + clearok(curscr,TRUE); + wrefresh(curscr); + when 'v': + after = FALSE; + msg(rs,"version %s. (mctesq was here)", release); + when 'S': + after = FALSE; +#ifdef STANDALONE + save_game(rs); +#else + msg(rs,"Saving is disabled, use bailout rpc"); +#endif + when '.': ; /* Rest command */ + when ' ': after = FALSE; /* "Legal" illegal command */ + when '^': + after = FALSE; + if (get_dir(rs)) { + delta.y += hero.y; + delta.x += hero.x; + fp = &flat(delta.y, delta.x); + if (!terse) + addmsg(rs,"You have found "); + if (chat(delta.y, delta.x) != TRAP) + msg(rs,"no trap there"); + else if (on(player, ISHALU)) + msg(rs,(char *)tr_name[rnd(NTRAPS)]); + else { + msg(rs,(char *)tr_name[*fp & F_TMASK]); + *fp |= F_SEEN; + } + } +#ifdef MASTER + when '+': + after = FALSE; + if (wizard) + { + wizard = FALSE; + turn_see(rs,TRUE); + msg(rs,"not wizard any more"); + } + else + { + wizard = passwd(); + if (wizard) + { + noscore = TRUE; + turn_see(rs,FALSE); + msg(rs,"you are suddenly as smart as Ken Arnold in dungeon #%d", dnum); + } + else + msg(rs,"sorry"); + } +#endif + when ESCAPE: /* Escape */ + door_stop = FALSE; + count = 0; + after = FALSE; + again = FALSE; + when 'm': + move_on = TRUE; + if (!get_dir(rs)) + after = FALSE; + else + { + ch = dir_ch; + countch = dir_ch; + goto over; + } + when ')': current(rs,cur_weapon, "wielding", NULL); + when ']': current(rs,cur_armor, "wearing", NULL); + when '=': + current(rs,cur_ring[LEFT], "wearing", + terse ? (char *)"(L)" : (char *)"on left hand"); + current(rs,cur_ring[RIGHT], "wearing", + terse ? (char *)"(R)" : (char *)"on right hand"); + when '@': + stat_msg = TRUE; + status(rs); + stat_msg = FALSE; + after = FALSE; + otherwise: + after = FALSE; +#ifdef MASTER + if (wizard) switch (ch) + { + case '|': msg(rs,"@ %d,%d", hero.y, hero.x); + when 'C': create_obj(); + when '$': msg(rs,"inpack = %d", inpack); + when CTRL('G'): inventory(rs,lvl_obj, 0); + when CTRL('W'): whatis(rs,FALSE, 0); + when CTRL('D'): level++; new_level(); + when CTRL('A'): level--; new_level(); + when CTRL('F'): show_map(); + when CTRL('T'): teleport(); + when CTRL('E'): msg(rs,"food left: %d", food_left); + when CTRL('C'): add_pass(); + when CTRL('X'): turn_see(rs,on(player, SEEMONST)); + when CTRL('~'): + { + THING *item; + + if ((item = get_item(rs,"charge", STICK)) != NULL) + item->o_charges = 10000; + } + when CTRL('I'): + { + int i; + THING *obj; + + for (i = 0; i < 9; i++) + raise_level(rs); + /* + * Give him a sword (+1,+1) + */ + obj = new_item(); + init_weapon(obj, TWOSWORD); + obj->o_hplus = 1; + obj->o_dplus = 1; + add_pack(rs,obj, TRUE); + cur_weapon = obj; + /* + * And his suit of armor + */ + obj = new_item(); + obj->o_type = ARMOR; + obj->o_which = PLATE_MAIL; + obj->o_arm = -5; + obj->o_flags |= ISKNOW; + obj->o_count = 1; + obj->o_group = 0; + cur_armor = obj; + add_pack(rs,obj, TRUE); + } + when '*' : + pr_list(); + otherwise: + illcom(rs,ch); + } + else +#endif + illcom(rs,ch); + } + /* + * turn off flags if no longer needed + */ + if (!running) + door_stop = FALSE; + } +/* + * If he ran into something to take, let him pick it up. + */ + if (take != 0) + pick_up(rs,take); + if (!running) + door_stop = FALSE; + if (!after) + ntimes++; + } + do_daemons(rs,AFTER); + do_fuses(rs,AFTER); + if (ISRING(LEFT, R_SEARCH)) + search(rs); + else if (ISRING(LEFT, R_TELEPORT) && rnd(50) == 0) + teleport(rs); + if (ISRING(RIGHT, R_SEARCH)) + search(rs); + else if (ISRING(RIGHT, R_TELEPORT) && rnd(50) == 0) + teleport(rs); +} + +/* + * illcom: + * What to do with an illegal command + */ +void +illcom(struct rogue_state *rs,int ch) +{ + save_msg = FALSE; + count = 0; + msg(rs,"illegal command '%s'", unctrl(ch)); + save_msg = TRUE; +} + +/* + * search: + * player gropes about him to find hidden things. + */ +void +search(struct rogue_state *rs) +{ + register int y, x; + register char *fp; + register int ey, ex; + int probinc; + bool found; + + ey = hero.y + 1; + ex = hero.x + 1; + probinc = (on(player, ISHALU) ? 3 : 0); + probinc += (on(player, ISBLIND) ? 2 : 0); + found = FALSE; + for (y = hero.y - 1; y <= ey; y++) + for (x = hero.x - 1; x <= ex; x++) + { + if (y == hero.y && x == hero.x) + continue; + fp = &flat(y, x); + if (!(*fp & F_REAL)) + switch (chat(y, x)) + { + case '|': + case '-': + if (rnd(5 + probinc) != 0) + break; + chat(y, x) = DOOR; + msg(rs,"a secret door"); +foundone: + found = TRUE; + *fp |= F_REAL; + count = FALSE; + running = FALSE; + break; + case FLOOR: + if (rnd(2 + probinc) != 0) + break; + chat(y, x) = TRAP; + if (!terse) + addmsg(rs,"you found "); + if (on(player, ISHALU)) + msg(rs,(char *)tr_name[rnd(NTRAPS)]); + else { + msg(rs,(char *)tr_name[*fp & F_TMASK]); + *fp |= F_SEEN; + } + goto foundone; + break; + case ' ': + if (rnd(3 + probinc) != 0) + break; + chat(y, x) = PASSAGE; + goto foundone; + } + } + if (found) + look(rs,FALSE); +} + +/* + * help: + * Give single character help, or the whole mess if he wants it + */ +void +help(struct rogue_state *rs) +{ + register const struct h_list *strp; + register char helpch; + register int numprint, cnt; + msg(rs,"character you want help for (* for all): "); + helpch = readchar(rs); + mpos = 0; + /* + * If its not a *, print the right help string + * or an error if he typed a funny character. + */ + if (helpch != '*') + { + move(0, 0); + for (strp = helpstr; strp->h_desc != NULL; strp++) + if (strp->h_ch == helpch) + { + lower_msg = TRUE; + msg(rs,"%s%s", unctrl(strp->h_ch), strp->h_desc); + lower_msg = FALSE; + return; + } + msg(rs,"unknown character '%s'", unctrl(helpch)); + return; + } + /* + * Here we print help for everything. + * Then wait before we return to command mode + */ + numprint = 0; + for (strp = helpstr; strp->h_desc != NULL; strp++) + if (strp->h_print) + numprint++; + if (numprint & 01) /* round odd numbers up */ + numprint++; + numprint /= 2; + if (numprint > LINES - 1) + numprint = LINES - 1; + + wclear(hw); + cnt = 0; + for (strp = helpstr; strp->h_desc != NULL; strp++) + if (strp->h_print) + { + wmove(hw, cnt % numprint, cnt >= numprint ? COLS / 2 : 0); + if (strp->h_ch) + waddstr(hw, unctrl(strp->h_ch)); + waddstr(hw, strp->h_desc); + if (++cnt >= numprint * 2) + break; + } + wmove(hw, LINES - 1, 0); + waddstr(hw, "--Press space to continue--"); + wrefresh(hw); + wait_for(rs,' '); + clearok(stdscr, TRUE); +/* + refresh(); +*/ + msg(rs,""); + touchwin(stdscr); + wrefresh(stdscr); +} + +/* + * identify: + * Tell the player what a certain thing is. + */ +void +identify(struct rogue_state *rs) +{ + register int ch; + register const struct h_list *hp; + register char *str; + static const struct h_list ident_list[] = { + {'|', "wall of a room", FALSE}, + {'-', "wall of a room", FALSE}, + {GOLD, "gold", FALSE}, + {STAIRS, "a staircase", FALSE}, + {DOOR, "door", FALSE}, + {FLOOR, "room floor", FALSE}, + {PLAYER, "you", FALSE}, + {PASSAGE, "passage", FALSE}, + {TRAP, "trap", FALSE}, + {POTION, "potion", FALSE}, + {SCROLL, "scroll", FALSE}, + {FOOD, "food", FALSE}, + {WEAPON, "weapon", FALSE}, + {' ', "solid rock", FALSE}, + {ARMOR, "armor", FALSE}, + {AMULET, "the Amulet of Yendor", FALSE}, + {RING, "ring", FALSE}, + {STICK, "wand or staff", FALSE}, + {'\0'} + }; + + msg(rs,"what do you want identified? "); + ch = readchar(rs); + mpos = 0; + if (ch == ESCAPE) + { + msg(rs,""); + return; + } + if (isupper(ch)) + str = monsters[ch-'A'].m_name; + else + { + str = "unknown character"; + for (hp = ident_list; hp->h_ch != '\0'; hp++) + if (hp->h_ch == ch) + { + str = hp->h_desc; + break; + } + } + msg(rs,"'%s': %s", unctrl(ch), str); +} + +/* + * d_level: + * He wants to go down a level + */ +void +d_level(struct rogue_state *rs) +{ + if (levit_check(rs)) + return; + if (chat(hero.y, hero.x) != STAIRS) + msg(rs,"I see no way down"); + else + { + level++; + seenstairs = FALSE; + new_level(rs); + } +} + +/* + * u_level: + * He wants to go up a level + */ +void +u_level(struct rogue_state *rs) +{ + if (levit_check(rs)) + return; + if (chat(hero.y, hero.x) == STAIRS) + if (amulet) + { + level--; + if (level == 0) + total_winner(rs); + new_level(rs); + msg(rs,"you feel a wrenching sensation in your gut"); + } + else + msg(rs,"your way is magically blocked"); + else + msg(rs,"I see no way up"); +} + +/* + * levit_check: + * Check to see if she's levitating, and if she is, print an + * appropriate message. + */ +bool +levit_check(struct rogue_state *rs) +{ + if (!on(player, ISLEVIT)) + return FALSE; + msg(rs,"You can't. You're floating off the ground!"); + return TRUE; +} + +/* + * call: + * Allow a user to call a potion, scroll, or ring something + */ +void +call(struct rogue_state *rs) +{ + register THING *obj; + register const struct obj_info *op = NULL; + register char **guess; const char *elsewise = NULL; + register const bool *know; + + obj = get_item(rs,"call", CALLABLE); + /* + * Make certain that it is somethings that we want to wear + */ + if (obj == NULL) + return; + switch (obj->o_type) + { + case RING: + op = &ring_info[obj->o_which]; + elsewise = r_stones[obj->o_which]; + goto norm; + when POTION: + op = &pot_info[obj->o_which]; + elsewise = p_colors[obj->o_which]; + goto norm; + when SCROLL: + op = &scr_info[obj->o_which]; + elsewise = s_names[obj->o_which]; + goto norm; + when STICK: + op = &ws_info[obj->o_which]; + elsewise = ws_made[obj->o_which]; +norm: + know = &op->oi_know; + guess = (char **)&op->oi_guess; + if (*guess != NULL) + elsewise = *guess; + when FOOD: + msg(rs,"you can't call that anything"); + return; + otherwise: + guess = &obj->o_label; + know = NULL; + elsewise = obj->o_label; + } + if (know != NULL && *know) + { + msg(rs,"that has already been identified"); + return; + } + if (elsewise != NULL && elsewise == *guess) + { + if (!terse) + addmsg(rs,"Was "); + msg(rs,"called \"%s\"", elsewise); + } + if (terse) + msg(rs,"call it: "); + else + msg(rs,"what do you want to call it? "); + + if (elsewise == NULL) + strcpy(prbuf, ""); + else + strcpy(prbuf, elsewise); + if (get_str(rs,prbuf, stdscr) == NORM) + { + if (*guess != NULL) + free(*guess); + *guess = (char *)malloc((unsigned int) strlen(prbuf) + 1); + strcpy(*guess, prbuf); + } +} + +/* + * current: + * Print the current weapon/armor + */ +void +current(struct rogue_state *rs,THING *cur, char *how, char *where) +{ + after = FALSE; + if (cur != NULL) + { + if (!terse) + addmsg(rs,"you are %s (", how); + inv_describe = FALSE; + addmsg(rs,"%c) %s", cur->o_packch, inv_name(cur, TRUE)); + inv_describe = TRUE; + if (where) + addmsg(rs," %s", where); + endmsg(rs); + } + else + { + if (!terse) + addmsg(rs,"you are "); + addmsg(rs,"%s nothing", how); + if (where) + addmsg(rs," %s", where); + endmsg(rs); + } +} diff --git a/src/cc/rogue/config.guess b/src/cc/rogue/config.guess new file mode 100755 index 000000000..396482d6c --- /dev/null +++ b/src/cc/rogue/config.guess @@ -0,0 +1,1500 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, +# Inc. + +timestamp='2006-07-02' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner . +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + x86:Interix*:[3456]*) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + EM64T:Interix*:[3456]*) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^CPU/{ + s: ::g + p + }'`" + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^CPU/{ + s: ::g + p + }'`" + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^LIBC/{ + s: ::g + p + }'`" + test x"${LIBC}" != x && { + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit + } + test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c < +# include +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 < in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/src/cc/rogue/config.h.in b/src/cc/rogue/config.h.in new file mode 100644 index 000000000..54f9b7cba --- /dev/null +++ b/src/cc/rogue/config.h.in @@ -0,0 +1,269 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define if scorefile is top scores, not top players */ +#undef ALLSCORES + +/* Define if checktime feature should be enabled */ +#undef CHECKTIME + +/* Define to group owner of setgid executable */ +#undef GROUPOWNER + +/* Define to 1 if you have the `alarm' function. */ +#undef HAVE_ALARM + +/* Define to 1 if you have the header file. */ +#undef HAVE_ARPA_INET_H + +/* Define to 1 if libcurses is requested */ +#undef HAVE_CURSES_H + +/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */ +#undef HAVE_DOPRNT + +/* Define to 1 if you have the `erasechar' function. */ +#undef HAVE_ERASECHAR + +/* Define if ncurses has ESCDELAY variable */ +#undef HAVE_ESCDELAY + +/* Define to 1 if you have the header file. */ +#undef HAVE_FCNTL_H + +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + +/* Define to 1 if you have the `getgid' function. */ +#undef HAVE_GETGID + +/* Define to 1 if you have the `getloadavg' function. */ +#undef HAVE_GETLOADAVG + +/* Define to 1 if you have the `getpass' function. */ +#undef HAVE_GETPASS + +/* Define to 1 if you have the `getpwuid' function. */ +#undef HAVE_GETPWUID + +/* Define to 1 if you have the `getuid' function. */ +#undef HAVE_GETUID + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `killchar' function. */ +#undef HAVE_KILLCHAR + +/* Define to 1 if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define to 1 if you have the `loadav' function. */ +#undef HAVE_LOADAV + +/* Define to 1 if `lstat' has the bug that it succeeds when given the + zero-length file name argument. */ +#undef HAVE_LSTAT_EMPTY_STRING_BUG + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* Define to 1 if libncurses is requested */ +#undef HAVE_NCURSES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_NCURSES_TERM_H + +/* Define to 1 if you have the `nlist' function. */ +#undef HAVE_NLIST + +/* Define to 1 if you have the header file. */ +#undef HAVE_NLIST_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PROCESS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_PWD_H + +/* Define to 1 if you have the `setenv' function. */ +#undef HAVE_SETENV + +/* Define to 1 if you have the `setgid' function. */ +#undef HAVE_SETGID + +/* Define to 1 if you have the `setregid' function. */ +#undef HAVE_SETREGID + +/* Define to 1 if you have the `setresgid' function. */ +#undef HAVE_SETRESGID + +/* Define to 1 if you have the `setresuid' function. */ +#undef HAVE_SETRESUID + +/* Define to 1 if you have the `setreuid' function. */ +#undef HAVE_SETREUID + +/* Define to 1 if you have the `setuid' function. */ +#undef HAVE_SETUID + +/* Define to 1 if you have the `spawnl' function. */ +#undef HAVE_SPAWNL + +/* Define to 1 if `stat' has the bug that it succeeds when given the + zero-length file name argument. */ +#undef HAVE_STAT_EMPTY_STRING_BUG + +/* Define to 1 if stdbool.h conforms to C99. */ +#undef HAVE_STDBOOL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strchr' function. */ +#undef HAVE_STRCHR + +/* Define to 1 if you have the `strerror' function. */ +#undef HAVE_STRERROR + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_IOCTL_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_UTSNAME_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERMIOS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_TERM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UTMP_H + +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + +/* Define to 1 if you have the header file. */ +#undef HAVE_VFORK_H + +/* Define to 1 if you have the `vprintf' function. */ +#undef HAVE_VPRINTF + +/* Define to 1 if `fork' works. */ +#undef HAVE_WORKING_FORK + +/* Define to 1 if `vfork' works. */ +#undef HAVE_WORKING_VFORK + +/* Define to 1 if the system has the type `_Bool'. */ +#undef HAVE__BOOL + +/* Define to 1 if you have the `_spawnl' function. */ +#undef HAVE__SPAWNL + +/* define if we should use program's load average function instead of system + */ +#undef LOADAV + +/* Define to file to use for scoreboard lockfile */ +#undef LOCKFILE + +/* Define to 1 if `lstat' dereferences a symlink specified with a trailing + slash. */ +#undef LSTAT_FOLLOWS_SLASHED_SYMLINK + +/* Define to include wizard mode */ +#undef MASTER + +/* Define if maxusers feature should be enabled */ +#undef MAXLOAD + +/* Define if maxusers feature should be enabled */ +#undef MAXUSERS + +/* kernel file to pass to nlist() when reading load average (unlikely to work) + */ +#undef NAMELIST + +/* word for the number of scores to store in scoreboard */ +#undef NUMNAME + +/* number of scores to store in scoreboard */ +#undef NUMSCORES + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define crypt(3) wizard mode password */ +#undef PASSWD + +/* Define as the return type of signal handlers (`int' or `void'). */ +#undef RETSIGTYPE + +/* Define to file to use for scoreboard */ +#undef SCOREFILE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Define to 1 if your declares `struct tm'. */ +#undef TM_IN_SYS_TIME + +/* define if we should use program's user counting function instead of + system's */ +#undef UCOUNT + +/* utmp like file to pass to ucount() when counting online users (unlikely to + work) */ +#undef UTMP + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `int' if doesn't define. */ +#undef gid_t + +/* Define to `int' if does not define. */ +#undef pid_t + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* Define to `int' if doesn't define. */ +#undef uid_t + +/* Define as `fork' if `vfork' does not work. */ +#undef vfork diff --git a/src/cc/rogue/config.sub b/src/cc/rogue/config.sub new file mode 100755 index 000000000..387c18d1a --- /dev/null +++ b/src/cc/rogue/config.sub @@ -0,0 +1,1608 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, +# Inc. + +timestamp='2006-07-02' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to . Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/src/cc/rogue/configure b/src/cc/rogue/configure new file mode 100755 index 000000000..a7b95e78d --- /dev/null +++ b/src/cc/rogue/configure @@ -0,0 +1,7511 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.61 for Rogue 5.4.4. +# +# Report bugs to . +# +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + +if test "x$CONFIG_SHELL" = x; then + if (eval ":") 2>/dev/null; then + as_have_required=yes +else + as_have_required=no +fi + + if test $as_have_required = yes && (eval ": +(as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=\$LINENO + as_lineno_2=\$LINENO + test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && + test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } +") 2> /dev/null; then + : +else + as_candidate_shells= + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + case $as_dir in + /*) + for as_base in sh bash ksh sh5; do + as_candidate_shells="$as_candidate_shells $as_dir/$as_base" + done;; + esac +done +IFS=$as_save_IFS + + + for as_shell in $as_candidate_shells $SHELL; do + # Try only shells that exist, to save several forks. + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { ("$as_shell") 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +_ASEOF +}; then + CONFIG_SHELL=$as_shell + as_have_required=yes + if { "$as_shell" 2> /dev/null <<\_ASEOF +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + +: +(as_func_return () { + (exit $1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = "$1" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test $exitcode = 0) || { (exit 1); exit 1; } + +( + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } + +_ASEOF +}; then + break +fi + +fi + + done + + if test "x$CONFIG_SHELL" != x; then + for as_var in BASH_ENV ENV + do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + done + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + + if test $as_have_required = no; then + echo This script requires a shell more modern than all the + echo shells that I found on your system. Please install a + echo modern shell, or manually run the script under such a + echo shell if you do have one. + { (exit 1); exit 1; } +fi + + +fi + +fi + + + +(eval "as_func_return () { + (exit \$1) +} +as_func_success () { + as_func_return 0 +} +as_func_failure () { + as_func_return 1 +} +as_func_ret_success () { + return 0 +} +as_func_ret_failure () { + return 1 +} + +exitcode=0 +if as_func_success; then + : +else + exitcode=1 + echo as_func_success failed. +fi + +if as_func_failure; then + exitcode=1 + echo as_func_failure succeeded. +fi + +if as_func_ret_success; then + : +else + exitcode=1 + echo as_func_ret_success failed. +fi + +if as_func_ret_failure; then + exitcode=1 + echo as_func_ret_failure succeeded. +fi + +if ( set x; as_func_ret_success y && test x = \"\$1\" ); then + : +else + exitcode=1 + echo positional parameters were not saved. +fi + +test \$exitcode = 0") || { + echo No shell found that supports shell functions. + echo Please tell autoconf@gnu.org about your system, + echo including any error possibly output before this + echo message +} + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + + +exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= +SHELL=${CONFIG_SHELL-/bin/sh} + +# Identity of this package. +PACKAGE_NAME='Rogue' +PACKAGE_TARNAME='rogue' +PACKAGE_VERSION='5.4.4' +PACKAGE_STRING='Rogue 5.4.4' +PACKAGE_BUGREPORT='yendor@rogueforge.net' + +ac_unique_file="armor.c" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='SHELL +PATH_SEPARATOR +PACKAGE_NAME +PACKAGE_TARNAME +PACKAGE_VERSION +PACKAGE_STRING +PACKAGE_BUGREPORT +exec_prefix +prefix +program_transform_name +bindir +sbindir +libexecdir +datarootdir +datadir +sysconfdir +sharedstatedir +localstatedir +includedir +oldincludedir +docdir +infodir +htmldir +dvidir +pdfdir +psdir +libdir +localedir +mandir +DEFS +ECHO_C +ECHO_N +ECHO_T +LIBS +build_alias +host_alias +target_alias +build +build_cpu +build_vendor +build_os +host +host_cpu +host_vendor +host_os +target +target_cpu +target_vendor +target_os +CC +CFLAGS +LDFLAGS +CPPFLAGS +ac_ct_CC +EXEEXT +OBJEXT +CPP +GREP +EGREP +LIBOBJS +NROFF +GROFF +COLCRT +TBL +SED +PROGRAM +GROUPOWNER +SCOREFILE +LOCKFILE +TARGET +LTLIBOBJS' +ac_subst_files='' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid feature name: $ac_feature" >&2 + { (exit 1); exit 1; }; } + ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` + eval enable_$ac_feature=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=\$ac_optarg ;; + + -without-* | --without-*) + ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid package name: $ac_package" >&2 + { (exit 1); exit 1; }; } + ac_package=`echo $ac_package | sed 's/[-.]/_/g'` + eval with_$ac_package=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) { echo "$as_me: error: unrecognized option: $ac_option +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && + { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 + { (exit 1); exit 1; }; } + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + { echo "$as_me: error: missing argument to $ac_option" >&2 + { (exit 1); exit 1; }; } +fi + +# Be sure to have absolute directory names. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir +do + eval ac_val=\$$ac_var + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 + { (exit 1); exit 1; }; } +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + If a cross compiler is detected then cross compile mode will be used." >&2 + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + { echo "$as_me: error: Working directory cannot be determined" >&2 + { (exit 1); exit 1; }; } +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + { echo "$as_me: error: pwd does not report name of working directory" >&2 + { (exit 1); exit 1; }; } + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$0" || +$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$0" : 'X\(//\)[^/]' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X"$0" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 + { (exit 1); exit 1; }; } +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 + { (exit 1); exit 1; }; } + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures Rogue 5.4.4 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/rogue] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] + --target=TARGET configure for building compilers for TARGET [HOST] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of Rogue 5.4.4:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-setgid=NAME install executable as setgid with group ownership of + NAME [default=no]] + --enable-scorefile=SCOREFILE + enable scoreboard with given filename + --enable-lockfile=LOCKFILE + enable scoreboard lockfile with given filename + --enable-wizardmode enable availability of wizard mode [default=no] + --enable-allscores enable scoreboard to show top scores, not just top + players [default=yes] + --enable-checktime enable checktime [default=no] + --enable-maxload enable maxload [default=no] + --enable-maxusers enable maxuser [default=no] + --enable-numscores number of scores to store in scoreboard [default=10] + --enable-numname word for number of scores to store in scoreboard + [default=Ten] + --enable-loadav=NAMELIST + use program's load average function (unlikely to + work) [default=no] + --enable-ucount=UTMPFILE + use program's own function to count users (unlikely + to work) [default=no] + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-ncurses Force the use of ncurses over curses + --with-program-name=NAME + alternate executable name + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +Rogue configure 5.4.4 +generated by GNU Autoconf 2.61 + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, +2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by Rogue $as_me 5.4.4, which was +generated by GNU Autoconf 2.61. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + echo "PATH: $as_dir" +done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 2) + ac_configure_args1="$ac_configure_args1 '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + ac_configure_args="$ac_configure_args '$ac_arg'" + ;; + esac + done +done +$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } +$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + cat <<\_ASBOX +## ---------------- ## +## Cache variables. ## +## ---------------- ## +_ASBOX + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + cat <<\_ASBOX +## ----------------- ## +## Output variables. ## +## ----------------- ## +_ASBOX + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + cat <<\_ASBOX +## ------------------- ## +## File substitutions. ## +## ------------------- ## +_ASBOX + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + cat <<\_ASBOX +## ----------- ## +## confdefs.h. ## +## ----------- ## +_ASBOX + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + echo "$as_me: caught signal $ac_signal" + echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + set x "$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + set x "$prefix/share/config.site" "$prefix/etc/config.site" +else + set x "$ac_default_prefix/share/config.site" \ + "$ac_default_prefix/etc/config.site" +fi +shift +for ac_site_file +do + if test -r "$ac_site_file"; then + { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 +echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special + # files actually), so we avoid doing that. + if test -f "$cache_file"; then + { echo "$as_me:$LINENO: loading cache $cache_file" >&5 +echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { echo "$as_me:$LINENO: creating cache $cache_file" >&5 +echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 +echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 +echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 +echo "$as_me: former value: $ac_old_val" >&2;} + { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 +echo "$as_me: current value: $ac_new_val" >&2;} + ac_cache_corrupted=: + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 +echo "$as_me: error: changes in the environment can compromise the build" >&2;} + { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 +echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} + { (exit 1); exit 1; }; } +fi + + + + + + + + + + + + + + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_config_headers="$ac_config_headers config.h" + +ac_config_files="$ac_config_files Makefile rogue.6 rogue.cat rogue.doc rogue.html rogue.me" + +ac_aux_dir= +for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&5 +echo "$as_me: error: cannot find install-sh or install.sh in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" >&2;} + { (exit 1); exit 1; }; } +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5 +echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;} + { (exit 1); exit 1; }; } + +{ echo "$as_me:$LINENO: checking build system type" >&5 +echo $ECHO_N "checking build system type... $ECHO_C" >&6; } +if test "${ac_cv_build+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 +echo "$as_me: error: cannot guess build type; you must specify one" >&2;} + { (exit 1); exit 1; }; } +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5 +echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;} + { (exit 1); exit 1; }; } + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_build" >&5 +echo "${ECHO_T}$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5 +echo "$as_me: error: invalid value of canonical build" >&2;} + { (exit 1); exit 1; }; };; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ echo "$as_me:$LINENO: checking host system type" >&5 +echo $ECHO_N "checking host system type... $ECHO_C" >&6; } +if test "${ac_cv_host+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5 +echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_host" >&5 +echo "${ECHO_T}$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) { { echo "$as_me:$LINENO: error: invalid value of canonical host" >&5 +echo "$as_me: error: invalid value of canonical host" >&2;} + { (exit 1); exit 1; }; };; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + +{ echo "$as_me:$LINENO: checking target system type" >&5 +echo $ECHO_N "checking target system type... $ECHO_C" >&6; } +if test "${ac_cv_target+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "x$target_alias" = x; then + ac_cv_target=$ac_cv_host +else + ac_cv_target=`$SHELL "$ac_aux_dir/config.sub" $target_alias` || + { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&5 +echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $target_alias failed" >&2;} + { (exit 1); exit 1; }; } +fi + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_target" >&5 +echo "${ECHO_T}$ac_cv_target" >&6; } +case $ac_cv_target in +*-*-*) ;; +*) { { echo "$as_me:$LINENO: error: invalid value of canonical target" >&5 +echo "$as_me: error: invalid value of canonical target" >&2;} + { (exit 1); exit 1; }; };; +esac +target=$ac_cv_target +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_target +shift +target_cpu=$1 +target_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +target_os=$* +IFS=$ac_save_IFS +case $target_os in *\ *) target_os=`echo "$target_os" | sed 's/ /-/g'`;; esac + + +# The aliases save the names the user supplied, while $host etc. +# will get canonicalized. +test -n "$target_alias" && + test "$program_prefix$program_suffix$program_transform_name" = \ + NONENONEs,x,x, && + program_prefix=${target_alias}- + +# Checks for programs. +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="gcc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { echo "$as_me:$LINENO: result: $CC" >&5 +echo "${ECHO_T}$CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CC="$ac_prog" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 +echo "${ECHO_T}$ac_ct_CC" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&5 +echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools +whose name does not start with the host triplet. If you think this +configuration is useful to you, please write to autoconf@gnu.org." >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&5 +echo "$as_me: error: no acceptable C compiler found in \$PATH +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + +# Provide some information about the compiler. +echo "$as_me:$LINENO: checking for C compiler version" >&5 +ac_compiler=`set X $ac_compile; echo $2` +{ (ac_try="$ac_compiler --version >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler --version >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -v >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -v >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } +{ (ac_try="$ac_compiler -V >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compiler -V >&5") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 +echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } +ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` +# +# List of possible output files, starting from the most likely. +# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) +# only as a last resort. b.out is created by i960 compilers. +ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' +# +# The IRIX 6 linker writes into existing files which may not be +# executable, retaining their permissions. Remove them first so a +# subsequent execution test works. +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { (ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi + +{ echo "$as_me:$LINENO: result: $ac_file" >&5 +echo "${ECHO_T}$ac_file" >&6; } +if test -z "$ac_file"; then + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: C compiler cannot create executables +See \`config.log' for more details." >&5 +echo "$as_me: error: C compiler cannot create executables +See \`config.log' for more details." >&2;} + { (exit 77); exit 77; }; } +fi + +ac_exeext=$ac_cv_exeext + +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 +echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } +# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 +# If not cross compiling, check that we can run a simple program. +if test "$cross_compiling" != yes; then + if { ac_try='./$ac_file' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { echo "$as_me:$LINENO: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } + fi + fi +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } + +rm -f a.out a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 +echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } +{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 +echo "${ECHO_T}$cross_compiling" >&6; } + +{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 +echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest$ac_cv_exeext +{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 +echo "${ECHO_T}$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 +echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } +if test "${ac_cv_objext+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; then + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&5 +echo "$as_me: error: cannot compute suffix of object files: cannot compile +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 +echo "${ECHO_T}$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 +echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_compiler_gnu=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_compiler_gnu=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 +echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } +GCC=`test $ac_compiler_gnu = yes && echo yes` +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 +echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + CFLAGS="" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_g=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 +echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_prog_cc_c89=$ac_arg +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + +fi + +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { echo "$as_me:$LINENO: result: none needed" >&5 +echo "${ECHO_T}none needed" >&6; } ;; + xno) + { echo "$as_me:$LINENO: result: unsupported" >&5 +echo "${ECHO_T}unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 +echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; +esac + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# Checks for libraries. + +# Checks for header files. + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 +echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if test "${ac_cv_prog_CPP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ echo "$as_me:$LINENO: result: $CPP" >&5 +echo "${ECHO_T}$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + : +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Broken: fails on valid input. +continue +fi + +rm -f conftest.err conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + # Broken: success on invalid input. +continue +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + # Passes both tests. +ac_preproc_ok=: +break +fi + +rm -f conftest.err conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then + : +else + { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&5 +echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." >&2;} + { (exit 1); exit 1; }; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 +echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + # Extract the first word of "grep ggrep" to use in msg output +if test -z "$GREP"; then +set dummy grep ggrep; ac_prog_name=$2 +if test "${ac_cv_path_GREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_GREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue + # Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_GREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +GREP="$ac_cv_path_GREP" +if test -z "$GREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_GREP=$GREP +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 +echo "${ECHO_T}$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ echo "$as_me:$LINENO: checking for egrep" >&5 +echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + # Extract the first word of "egrep" to use in msg output +if test -z "$EGREP"; then +set dummy egrep; ac_prog_name=$2 +if test "${ac_cv_path_EGREP+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_path_EGREP_found=false +# Loop through the user's path and test for each of PROGNAME-LIST +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue + # Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + ac_count=`expr $ac_count + 1` + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + + $ac_path_EGREP_found && break 3 + done +done + +done +IFS=$as_save_IFS + + +fi + +EGREP="$ac_cv_path_EGREP" +if test -z "$EGREP"; then + { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 +echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} + { (exit 1); exit 1; }; } +fi + +else + ac_cv_path_EGREP=$EGREP +fi + + + fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 +echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 +echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } +if test "${ac_cv_header_stdc+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdc=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then + : +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then + : +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + : +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 +echo "${ECHO_T}$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +cat >>confdefs.h <<\_ACEOF +#define STDC_HEADERS 1 +_ACEOF + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. + + + + + + + + + +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default + +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + eval "$as_ac_Header=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_Header=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + + + + + + + + + + + + + + + +for ac_header in arpa/inet.h sys/utsname.h pwd.h fcntl.h limits.h nlist.h stdlib.h string.h sys/ioctl.h termios.h unistd.h utmp.h term.h ncurses/term.h process.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------ ## +## Report this to yendor@rogueforge.net ## +## ------------------------------------ ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +# Checks for typedefs, structures, and compiler characteristics. +{ echo "$as_me:$LINENO: checking for stdbool.h that conforms to C99" >&5 +echo $ECHO_N "checking for stdbool.h that conforms to C99... $ECHO_C" >&6; } +if test "${ac_cv_header_stdbool_h+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +#include +#ifndef bool + "error: bool is not defined" +#endif +#ifndef false + "error: false is not defined" +#endif +#if false + "error: false is not 0" +#endif +#ifndef true + "error: true is not defined" +#endif +#if true != 1 + "error: true is not 1" +#endif +#ifndef __bool_true_false_are_defined + "error: __bool_true_false_are_defined is not defined" +#endif + + struct s { _Bool s: 1; _Bool t; } s; + + char a[true == 1 ? 1 : -1]; + char b[false == 0 ? 1 : -1]; + char c[__bool_true_false_are_defined == 1 ? 1 : -1]; + char d[(bool) 0.5 == true ? 1 : -1]; + bool e = &s; + char f[(_Bool) 0.0 == false ? 1 : -1]; + char g[true]; + char h[sizeof (_Bool)]; + char i[sizeof s.t]; + enum { j = false, k = true, l = false * true, m = true * 256 }; + _Bool n[m]; + char o[sizeof n == m * sizeof n[0] ? 1 : -1]; + char p[-1 - (_Bool) 0 < 0 && -1 - (bool) 0 < 0 ? 1 : -1]; +# if defined __xlc__ || defined __GNUC__ + /* Catch a bug in IBM AIX xlc compiler version 6.0.0.0 + reported by James Lemley on 2005-10-05; see + http://lists.gnu.org/archive/html/bug-coreutils/2005-10/msg00086.html + This test is not quite right, since xlc is allowed to + reject this program, as the initializer for xlcbug is + not one of the forms that C requires support for. + However, doing the test right would require a runtime + test, and that would make cross-compilation harder. + Let us hope that IBM fixes the xlc bug, and also adds + support for this kind of constant expression. In the + meantime, this test will reject xlc, which is OK, since + our stdbool.h substitute should suffice. We also test + this with GCC, where it should work, to detect more + quickly whether someone messes up the test in the + future. */ + char digs[] = "0123456789"; + int xlcbug = 1 / (&(digs + 5)[-2 + (bool) 1] == &digs[4] ? 1 : -1); +# endif + /* Catch a bug in an HP-UX C compiler. See + http://gcc.gnu.org/ml/gcc-patches/2003-12/msg02303.html + http://lists.gnu.org/archive/html/bug-coreutils/2005-11/msg00161.html + */ + _Bool q = true; + _Bool *pq = &q; + +int +main () +{ + + *pq |= q; + *pq |= ! q; + /* Refer to every declared value, to avoid compiler optimizations. */ + return (!a + !b + !c + !d + !e + !f + !g + !h + !i + !!j + !k + !!l + + !m + !n + !o + !p + !q + !pq); + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_header_stdbool_h=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_header_stdbool_h=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_header_stdbool_h" >&5 +echo "${ECHO_T}$ac_cv_header_stdbool_h" >&6; } +{ echo "$as_me:$LINENO: checking for _Bool" >&5 +echo $ECHO_N "checking for _Bool... $ECHO_C" >&6; } +if test "${ac_cv_type__Bool+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +typedef _Bool ac__type_new_; +int +main () +{ +if ((ac__type_new_ *) 0) + return 0; +if (sizeof (ac__type_new_)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type__Bool=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type__Bool=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type__Bool" >&5 +echo "${ECHO_T}$ac_cv_type__Bool" >&6; } +if test $ac_cv_type__Bool = yes; then + +cat >>confdefs.h <<_ACEOF +#define HAVE__BOOL 1 +_ACEOF + + +fi + +if test $ac_cv_header_stdbool_h = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_STDBOOL_H 1 +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking for an ANSI C-conforming const" >&5 +echo $ECHO_N "checking for an ANSI C-conforming const... $ECHO_C" >&6; } +if test "${ac_cv_c_const+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ +/* FIXME: Include the comments suggested by Paul. */ +#ifndef __cplusplus + /* Ultrix mips cc rejects this. */ + typedef int charset[2]; + const charset cs; + /* SunOS 4.1.1 cc rejects this. */ + char const *const *pcpcc; + char **ppc; + /* NEC SVR4.0.2 mips cc rejects this. */ + struct point {int x, y;}; + static struct point const zero = {0,0}; + /* AIX XL C 1.02.0.0 rejects this. + It does not let you subtract one const X* pointer from another in + an arm of an if-expression whose if-part is not a constant + expression */ + const char *g = "string"; + pcpcc = &g + (g ? g-g : 0); + /* HPUX 7.0 cc rejects these. */ + ++pcpcc; + ppc = (char**) pcpcc; + pcpcc = (char const *const *) ppc; + { /* SCO 3.2v4 cc rejects this. */ + char *t; + char const *s = 0 ? (char *) 0 : (char const *) 0; + + *t++ = 0; + if (s) return 0; + } + { /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */ + int x[] = {25, 17}; + const int *foo = &x[0]; + ++foo; + } + { /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */ + typedef const int *iptr; + iptr p = 0; + ++p; + } + { /* AIX XL C 1.02.0.0 rejects this saying + "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */ + struct s { int j; const int *ap[3]; }; + struct s *b; b->j = 5; + } + { /* ULTRIX-32 V3.1 (Rev 9) vcc rejects this */ + const int foo = 10; + if (!foo) return 0; + } + return !cs[0] && !zero.x; +#endif + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_c_const=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_c_const=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_c_const" >&5 +echo "${ECHO_T}$ac_cv_c_const" >&6; } +if test $ac_cv_c_const = no; then + +cat >>confdefs.h <<\_ACEOF +#define const +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking for uid_t in sys/types.h" >&5 +echo $ECHO_N "checking for uid_t in sys/types.h... $ECHO_C" >&6; } +if test "${ac_cv_type_uid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "uid_t" >/dev/null 2>&1; then + ac_cv_type_uid_t=yes +else + ac_cv_type_uid_t=no +fi +rm -f conftest* + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type_uid_t" >&5 +echo "${ECHO_T}$ac_cv_type_uid_t" >&6; } +if test $ac_cv_type_uid_t = no; then + +cat >>confdefs.h <<\_ACEOF +#define uid_t int +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define gid_t int +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking for size_t" >&5 +echo $ECHO_N "checking for size_t... $ECHO_C" >&6; } +if test "${ac_cv_type_size_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +typedef size_t ac__type_new_; +int +main () +{ +if ((ac__type_new_ *) 0) + return 0; +if (sizeof (ac__type_new_)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_size_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_size_t=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type_size_t" >&5 +echo "${ECHO_T}$ac_cv_type_size_t" >&6; } +if test $ac_cv_type_size_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define size_t unsigned int +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking whether struct tm is in sys/time.h or time.h" >&5 +echo $ECHO_N "checking whether struct tm is in sys/time.h or time.h... $ECHO_C" >&6; } +if test "${ac_cv_struct_tm+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +struct tm tm; + int *p = &tm.tm_sec; + return !p; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_struct_tm=time.h +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_struct_tm=sys/time.h +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_struct_tm" >&5 +echo "${ECHO_T}$ac_cv_struct_tm" >&6; } +if test $ac_cv_struct_tm = sys/time.h; then + +cat >>confdefs.h <<\_ACEOF +#define TM_IN_SYS_TIME 1 +_ACEOF + +fi + + +# Check whether --with-ncurses was given. +if test "${with_ncurses+set}" = set; then + withval=$with_ncurses; +fi + + mp_save_LIBS="$LIBS" + CURSES_LIB="" + if test "$with_ncurses" != yes + then + { echo "$as_me:$LINENO: checking for working curses" >&5 +echo $ECHO_N "checking for working curses... $ECHO_C" >&6; } +if test "${mp_cv_curses+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + LIBS="$LIBS -lcurses" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +chtype a; int b=A_STANDOUT, c=KEY_LEFT; initscr(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + mp_cv_curses=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + mp_cv_curses=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $mp_cv_curses" >&5 +echo "${ECHO_T}$mp_cv_curses" >&6; } + if test "$mp_cv_curses" = yes + then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CURSES_H 1 +_ACEOF + + #AC_DEFINE(HAVE_CURSES_H) + CURSES_LIB="-lcurses" + fi + fi + if test ! "$CURSES_LIB" + then + { echo "$as_me:$LINENO: checking for working ncurses" >&5 +echo $ECHO_N "checking for working ncurses... $ECHO_C" >&6; } +if test "${mp_cv_ncurses+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + LIBS="$mp_save_LIBS -lncurses" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +chtype a; int b=A_STANDOUT, c=KEY_LEFT; initscr(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + mp_cv_ncurses=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + mp_cv_ncurses=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $mp_cv_ncurses" >&5 +echo "${ECHO_T}$mp_cv_ncurses" >&6; } + if test "$mp_cv_ncurses" = yes + then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_NCURSES_H 1 +_ACEOF + + CURSES_LIB="-lncurses" + fi + fi + if test ! "$CURSES_LIB" + then + { echo "$as_me:$LINENO: checking for working pdcurses" >&5 +echo $ECHO_N "checking for working pdcurses... $ECHO_C" >&6; } +if test "${mp_cv_pdcurses+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + LIBS="$mp_save_LIBS -lpdcurses" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +chtype a; int b=A_STANDOUT, c=KEY_LEFT; initscr(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + mp_cv_pdcurses=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + mp_cv_pdcurses=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $mp_cv_pdcurses" >&5 +echo "${ECHO_T}$mp_cv_pdcurses" >&6; } + if test "$mp_cv_pdcurses" = yes + then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CURSES_H 1 +_ACEOF + + CURSES_LIB="-lpdcurses" + fi + fi + if test ! "$CURSES_LIB" + then + { echo "$as_me:$LINENO: checking for working pdcur" >&5 +echo $ECHO_N "checking for working pdcur... $ECHO_C" >&6; } +if test "${mp_cv_pdcur+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + LIBS="$mp_save_LIBS -lpdcur" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +int +main () +{ +chtype a; int b=A_STANDOUT, c=KEY_LEFT; initscr(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + mp_cv_pdcur=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + mp_cv_pdcur=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $mp_cv_pdcur" >&5 +echo "${ECHO_T}$mp_cv_pdcur" >&6; } + if test "$mp_cv_pdcur" = yes + then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CURSES_H 1 +_ACEOF + + CURSES_LIB="-lpdcur" + fi + fi + if test ! "$CURSES_LIB" + then + { echo "$as_me:$LINENO: checking for working peer pdcurses" >&5 +echo $ECHO_N "checking for working peer pdcurses... $ECHO_C" >&6; } +if test "${mp_cv_lpdcurses+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + LIBS="$mp_save_LIBS ../pdcurses/pdcurses.a" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include "../pdcurses/curses.h" +int +main () +{ +chtype a; int b=A_STANDOUT, c=KEY_LEFT; initscr(); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + mp_cv_lpdcurses=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + mp_cv_lpdcurses=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $mp_cv_lpdcurses" >&5 +echo "${ECHO_T}$mp_cv_lpdcurses" >&6; } + if test "$mp_cv_lpdcurses" = yes + then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_CURSES_H 1 +_ACEOF + + CURSES_LIB="../pdcurses/pdcurses.a" + + if test "x$CPPFLAGS" = "x"; then + test "x$silent" != "xyes" && echo " setting CPPFLAGS to \""-I../pdcurses"\"" + CPPFLAGS=""-I../pdcurses"" + else + apr_addto_bugger=""-I../pdcurses"" + for i in $apr_addto_bugger; do + apr_addto_duplicate="0" + for j in $CPPFLAGS; do + if test "x$i" = "x$j"; then + apr_addto_duplicate="1" + break + fi + done + if test $apr_addto_duplicate = "0"; then + test "x$silent" != "xyes" && echo " adding \"$i\" to CPPFLAGS" + CPPFLAGS="$CPPFLAGS $i" + fi + done + fi + + fi + fi + { echo "$as_me:$LINENO: checking for ESCDELAY variable" >&5 +echo $ECHO_N "checking for ESCDELAY variable... $ECHO_C" >&6; } +if test "${mc_cv_ncurses_escdelay+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ + +int +main () +{ + + extern int ESCDELAY; + ESCDELAY = 0; + + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + mc_cv_ncurses_escdelay=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + mc_cv_ncurses_escdelay=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext + +fi +{ echo "$as_me:$LINENO: result: $mc_cv_ncurses_escdelay" >&5 +echo "${ECHO_T}$mc_cv_ncurses_escdelay" >&6; } + if test "$mc_cv_ncurses_escdelay" = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_ESCDELAY 1 +_ACEOF + + fi + if test ! "$CURSES_LIB" ; then + LIBS="$mp_save_LIBS" + fi + +# Checks for library functions. +{ echo "$as_me:$LINENO: checking for pid_t" >&5 +echo $ECHO_N "checking for pid_t... $ECHO_C" >&6; } +if test "${ac_cv_type_pid_t+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +typedef pid_t ac__type_new_; +int +main () +{ +if ((ac__type_new_ *) 0) + return 0; +if (sizeof (ac__type_new_)) + return 0; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_pid_t=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_pid_t=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type_pid_t" >&5 +echo "${ECHO_T}$ac_cv_type_pid_t" >&6; } +if test $ac_cv_type_pid_t = yes; then + : +else + +cat >>confdefs.h <<_ACEOF +#define pid_t int +_ACEOF + +fi + + +for ac_header in vfork.h +do +as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + { echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +else + # Is the header compilable? +{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 +echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +#include <$ac_header> +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_header_compiler=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_compiler=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 +echo "${ECHO_T}$ac_header_compiler" >&6; } + +# Is the header present? +{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 +echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } +cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include <$ac_header> +_ACEOF +if { (ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then + ac_header_preproc=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_header_preproc=no +fi + +rm -f conftest.err conftest.$ac_ext +{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 +echo "${ECHO_T}$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in + yes:no: ) + { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 +echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} + ac_header_preproc=yes + ;; + no:yes:* ) + { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 +echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 +echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 +echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 +echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 +echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} + { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 +echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} + ( cat <<\_ASBOX +## ------------------------------------ ## +## Report this to yendor@rogueforge.net ## +## ------------------------------------ ## +_ASBOX + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac +{ echo "$as_me:$LINENO: checking for $ac_header" >&5 +echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } +if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + eval "$as_ac_Header=\$ac_header_preproc" +fi +ac_res=`eval echo '${'$as_ac_Header'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } + +fi +if test `eval echo '${'$as_ac_Header'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + + +for ac_func in fork vfork +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + +if test "x$ac_cv_func_fork" = xyes; then + { echo "$as_me:$LINENO: checking for working fork" >&5 +echo $ECHO_N "checking for working fork... $ECHO_C" >&6; } +if test "${ac_cv_func_fork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_fork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ + + /* By Ruediger Kuhlmann. */ + return fork () < 0; + + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_fork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_fork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_fork_works" >&5 +echo "${ECHO_T}$ac_cv_func_fork_works" >&6; } + +else + ac_cv_func_fork_works=$ac_cv_func_fork +fi +if test "x$ac_cv_func_fork_works" = xcross; then + case $host in + *-*-amigaos* | *-*-msdosdjgpp*) + # Override, as these systems have only a dummy fork() stub + ac_cv_func_fork_works=no + ;; + *) + ac_cv_func_fork_works=yes + ;; + esac + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_fork_works guessed because of cross compilation" >&2;} +fi +ac_cv_func_vfork_works=$ac_cv_func_vfork +if test "x$ac_cv_func_vfork" = xyes; then + { echo "$as_me:$LINENO: checking for working vfork" >&5 +echo $ECHO_N "checking for working vfork... $ECHO_C" >&6; } +if test "${ac_cv_func_vfork_works+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_vfork_works=cross +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Thanks to Paul Eggert for this test. */ +$ac_includes_default +#include +#ifdef HAVE_VFORK_H +# include +#endif +/* On some sparc systems, changes by the child to local and incoming + argument registers are propagated back to the parent. The compiler + is told about this with #include , but some compilers + (e.g. gcc -O) don't grok . Test for this by using a + static variable whose address is put into a register that is + clobbered by the vfork. */ +static void +#ifdef __cplusplus +sparc_address_test (int arg) +# else +sparc_address_test (arg) int arg; +#endif +{ + static pid_t child; + if (!child) { + child = vfork (); + if (child < 0) { + perror ("vfork"); + _exit(2); + } + if (!child) { + arg = getpid(); + write(-1, "", 0); + _exit (arg); + } + } +} + +int +main () +{ + pid_t parent = getpid (); + pid_t child; + + sparc_address_test (0); + + child = vfork (); + + if (child == 0) { + /* Here is another test for sparc vfork register problems. This + test uses lots of local variables, at least as many local + variables as main has allocated so far including compiler + temporaries. 4 locals are enough for gcc 1.40.3 on a Solaris + 4.1.3 sparc, but we use 8 to be safe. A buggy compiler should + reuse the register of parent for one of the local variables, + since it will think that parent can't possibly be used any more + in this routine. Assigning to the local variable will thus + munge parent in the parent process. */ + pid_t + p = getpid(), p1 = getpid(), p2 = getpid(), p3 = getpid(), + p4 = getpid(), p5 = getpid(), p6 = getpid(), p7 = getpid(); + /* Convince the compiler that p..p7 are live; otherwise, it might + use the same hardware register for all 8 local variables. */ + if (p != p1 || p != p2 || p != p3 || p != p4 + || p != p5 || p != p6 || p != p7) + _exit(1); + + /* On some systems (e.g. IRIX 3.3), vfork doesn't separate parent + from child file descriptors. If the child closes a descriptor + before it execs or exits, this munges the parent's descriptor + as well. Test for this by closing stdout in the child. */ + _exit(close(fileno(stdout)) != 0); + } else { + int status; + struct stat st; + + while (wait(&status) != child) + ; + return ( + /* Was there some problem with vforking? */ + child < 0 + + /* Did the child fail? (This shouldn't happen.) */ + || status + + /* Did the vfork/compiler bug occur? */ + || parent != getpid() + + /* Did the file descriptor bug occur? */ + || fstat(fileno(stdout), &st) != 0 + ); + } +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_vfork_works=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_vfork_works=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_vfork_works" >&5 +echo "${ECHO_T}$ac_cv_func_vfork_works" >&6; } + +fi; +if test "x$ac_cv_func_fork_works" = xcross; then + ac_cv_func_vfork_works=$ac_cv_func_vfork + { echo "$as_me:$LINENO: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&5 +echo "$as_me: WARNING: result $ac_cv_func_vfork_works guessed because of cross compilation" >&2;} +fi + +if test "x$ac_cv_func_vfork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_VFORK 1 +_ACEOF + +else + +cat >>confdefs.h <<\_ACEOF +#define vfork fork +_ACEOF + +fi +if test "x$ac_cv_func_fork_works" = xyes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_WORKING_FORK 1 +_ACEOF + +fi + +if test $ac_cv_c_compiler_gnu = yes; then + { echo "$as_me:$LINENO: checking whether $CC needs -traditional" >&5 +echo $ECHO_N "checking whether $CC needs -traditional... $ECHO_C" >&6; } +if test "${ac_cv_prog_gcc_traditional+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + ac_pattern="Autoconf.*'x'" + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TIOCGETP +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +else + ac_cv_prog_gcc_traditional=no +fi +rm -f conftest* + + + if test $ac_cv_prog_gcc_traditional = no; then + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +Autoconf TCGETA +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "$ac_pattern" >/dev/null 2>&1; then + ac_cv_prog_gcc_traditional=yes +fi +rm -f conftest* + + fi +fi +{ echo "$as_me:$LINENO: result: $ac_cv_prog_gcc_traditional" >&5 +echo "${ECHO_T}$ac_cv_prog_gcc_traditional" >&6; } + if test $ac_cv_prog_gcc_traditional = yes; then + CC="$CC -traditional" + fi +fi + +{ echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 +echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6; } +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + return lstat ("conftest.sym/", &sbuf) == 0; + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; } + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + case " $LIBOBJS " in + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" + ;; +esac + +fi + +{ echo "$as_me:$LINENO: checking whether lstat accepts an empty string" >&5 +echo $ECHO_N "checking whether lstat accepts an empty string... $ECHO_C" >&6; } +if test "${ac_cv_func_lstat_empty_string_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_empty_string_bug=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + return lstat ("", &sbuf) == 0; + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_empty_string_bug=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_empty_string_bug=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_lstat_empty_string_bug" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_empty_string_bug" >&6; } +if test $ac_cv_func_lstat_empty_string_bug = yes; then + case " $LIBOBJS " in + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" + ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_LSTAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + +{ echo "$as_me:$LINENO: checking whether lstat dereferences a symlink specified with a trailing slash" >&5 +echo $ECHO_N "checking whether lstat dereferences a symlink specified with a trailing slash... $ECHO_C" >&6; } +if test "${ac_cv_func_lstat_dereferences_slashed_symlink+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + rm -f conftest.sym conftest.file +echo >conftest.file +if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then + if test "$cross_compiling" = yes; then + ac_cv_func_lstat_dereferences_slashed_symlink=no +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + /* Linux will dereference the symlink and fail. + That is better in the sense that it means we will not + have to compile and use the lstat wrapper. */ + return lstat ("conftest.sym/", &sbuf) == 0; + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_lstat_dereferences_slashed_symlink=yes +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +else + # If the `ln -s' command failed, then we probably don't even + # have an lstat function. + ac_cv_func_lstat_dereferences_slashed_symlink=no +fi +rm -f conftest.sym conftest.file + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 +echo "${ECHO_T}$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; } + +test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && + +cat >>confdefs.h <<_ACEOF +#define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 +_ACEOF + + +if test $ac_cv_func_lstat_dereferences_slashed_symlink = no; then + case " $LIBOBJS " in + *" lstat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS lstat.$ac_objext" + ;; +esac + +fi + +{ echo "$as_me:$LINENO: checking return type of signal handlers" >&5 +echo $ECHO_N "checking return type of signal handlers... $ECHO_C" >&6; } +if test "${ac_cv_type_signal+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +#include +#include + +int +main () +{ +return *(signal (0, 0)) (0) == 1; + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext +if { (ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_compile") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then + ac_cv_type_signal=int +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_type_signal=void +fi + +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_type_signal" >&5 +echo "${ECHO_T}$ac_cv_type_signal" >&6; } + +cat >>confdefs.h <<_ACEOF +#define RETSIGTYPE $ac_cv_type_signal +_ACEOF + + +{ echo "$as_me:$LINENO: checking whether stat accepts an empty string" >&5 +echo $ECHO_N "checking whether stat accepts an empty string... $ECHO_C" >&6; } +if test "${ac_cv_func_stat_empty_string_bug+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test "$cross_compiling" = yes; then + ac_cv_func_stat_empty_string_bug=yes +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +$ac_includes_default +int +main () +{ +struct stat sbuf; + return stat ("", &sbuf) == 0; + ; + return 0; +} +_ACEOF +rm -f conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { ac_try='./conftest$ac_exeext' + { (case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); }; }; then + ac_cv_func_stat_empty_string_bug=no +else + echo "$as_me: program exited with status $ac_status" >&5 +echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +( exit $ac_status ) +ac_cv_func_stat_empty_string_bug=yes +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext +fi + + +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func_stat_empty_string_bug" >&5 +echo "${ECHO_T}$ac_cv_func_stat_empty_string_bug" >&6; } +if test $ac_cv_func_stat_empty_string_bug = yes; then + case " $LIBOBJS " in + *" stat.$ac_objext "* ) ;; + *) LIBOBJS="$LIBOBJS stat.$ac_objext" + ;; +esac + + +cat >>confdefs.h <<_ACEOF +#define HAVE_STAT_EMPTY_STRING_BUG 1 +_ACEOF + +fi + + +for ac_func in vprintf +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +{ echo "$as_me:$LINENO: checking for _doprnt" >&5 +echo $ECHO_N "checking for _doprnt... $ECHO_C" >&6; } +if test "${ac_cv_func__doprnt+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define _doprnt to an innocuous variant, in case declares _doprnt. + For example, HP-UX 11i declares gettimeofday. */ +#define _doprnt innocuous__doprnt + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char _doprnt (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef _doprnt + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char _doprnt (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub__doprnt || defined __stub____doprnt +choke me +#endif + +int +main () +{ +return _doprnt (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + ac_cv_func__doprnt=yes +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_cv_func__doprnt=no +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +{ echo "$as_me:$LINENO: result: $ac_cv_func__doprnt" >&5 +echo "${ECHO_T}$ac_cv_func__doprnt" >&6; } +if test $ac_cv_func__doprnt = yes; then + +cat >>confdefs.h <<\_ACEOF +#define HAVE_DOPRNT 1 +_ACEOF + +fi + +fi +done + + + + + + + + + + + + + + + + + + + + + + + + +for ac_func in erasechar killchar alarm getpass memset setenv strchr nlist _spawnl spawnl getpwuid loadav getloadavg strerror setresgid setregid setgid setresuid setreuid setuid getuid getgid +do +as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh` +{ echo "$as_me:$LINENO: checking for $ac_func" >&5 +echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6; } +if { as_var=$as_ac_var; eval "test \"\${$as_var+set}\" = set"; }; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + cat >conftest.$ac_ext <<_ACEOF +/* confdefs.h. */ +_ACEOF +cat confdefs.h >>conftest.$ac_ext +cat >>conftest.$ac_ext <<_ACEOF +/* end confdefs.h. */ +/* Define $ac_func to an innocuous variant, in case declares $ac_func. + For example, HP-UX 11i declares gettimeofday. */ +#define $ac_func innocuous_$ac_func + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $ac_func (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $ac_func + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $ac_func (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$ac_func || defined __stub___$ac_func +choke me +#endif + +int +main () +{ +return $ac_func (); + ; + return 0; +} +_ACEOF +rm -f conftest.$ac_objext conftest$ac_exeext +if { (ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 + (eval "$ac_link") 2>conftest.er1 + ac_status=$? + grep -v '^ *+' conftest.er1 >conftest.err + rm -f conftest.er1 + cat conftest.err >&5 + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && + $as_test_x conftest$ac_exeext; then + eval "$as_ac_var=yes" +else + echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + eval "$as_ac_var=no" +fi + +rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ + conftest$ac_exeext conftest.$ac_ext +fi +ac_res=`eval echo '${'$as_ac_var'}'` + { echo "$as_me:$LINENO: result: $ac_res" >&5 +echo "${ECHO_T}$ac_res" >&6; } +if test `eval echo '${'$as_ac_var'}'` = yes; then + cat >>confdefs.h <<_ACEOF +#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + +# Extract the first word of "nroff", so it can be a program name with args. +set dummy nroff; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_NROFF+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$NROFF"; then + ac_cv_prog_NROFF="$NROFF" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_NROFF="nroff" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +NROFF=$ac_cv_prog_NROFF +if test -n "$NROFF"; then + { echo "$as_me:$LINENO: result: $NROFF" >&5 +echo "${ECHO_T}$NROFF" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +# Extract the first word of "groff", so it can be a program name with args. +set dummy groff; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_GROFF+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$GROFF"; then + ac_cv_prog_GROFF="$GROFF" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_GROFF="groff" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +GROFF=$ac_cv_prog_GROFF +if test -n "$GROFF"; then + { echo "$as_me:$LINENO: result: $GROFF" >&5 +echo "${ECHO_T}$GROFF" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +# Extract the first word of "colcrt", so it can be a program name with args. +set dummy colcrt; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_COLCRT+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$COLCRT"; then + ac_cv_prog_COLCRT="$COLCRT" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_COLCRT="colcrt" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +COLCRT=$ac_cv_prog_COLCRT +if test -n "$COLCRT"; then + { echo "$as_me:$LINENO: result: $COLCRT" >&5 +echo "${ECHO_T}$COLCRT" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +# Extract the first word of "tbl", so it can be a program name with args. +set dummy tbl; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_TBL+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$TBL"; then + ac_cv_prog_TBL="$TBL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_TBL="tbl" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +TBL=$ac_cv_prog_TBL +if test -n "$TBL"; then + { echo "$as_me:$LINENO: result: $TBL" >&5 +echo "${ECHO_T}$TBL" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + +# Extract the first word of "sed", so it can be a program name with args. +set dummy sed; ac_word=$2 +{ echo "$as_me:$LINENO: checking for $ac_word" >&5 +echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } +if test "${ac_cv_prog_SED+set}" = set; then + echo $ECHO_N "(cached) $ECHO_C" >&6 +else + if test -n "$SED"; then + ac_cv_prog_SED="$SED" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_SED="sed" + echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done +done +IFS=$as_save_IFS + +fi +fi +SED=$ac_cv_prog_SED +if test -n "$SED"; then + { echo "$as_me:$LINENO: result: $SED" >&5 +echo "${ECHO_T}$SED" >&6; } +else + { echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + + +# Check whether --with-program-name was given. +if test "${with_program_name+set}" = set; then + withval=$with_program_name; progname="$withval" +else + progname="rogue" +fi + +PROGRAM=$progname + + +# Check whether --enable-setgid was given. +if test "${enable_setgid+set}" = set; then + enableval=$enable_setgid; +fi + +{ echo "$as_me:$LINENO: checking if using setgid execute bit" >&5 +echo $ECHO_N "checking if using setgid execute bit... $ECHO_C" >&6; } +if test "x$enable_setgid" = "xno" ; then +GROUPOWNER= +elif test "x$enable_setgid" = "xyes" ; then +GROUPOWNER=games +elif test "x$enable_setgid" = "x" ; then +GROUPOWNER= +else +GROUPOWNER=$enable_setgid +fi + +if test "x$GROUPOWNER" != "x" ; then + +cat >>confdefs.h <<_ACEOF +#define GROUPOWNER $GROUPOWNER +_ACEOF + +{ echo "$as_me:$LINENO: result: $GROUPOWNER" >&5 +echo "${ECHO_T}$GROUPOWNER" >&6; } +else +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + + + +# Check whether --enable-scorefile was given. +if test "${enable_scorefile+set}" = set; then + enableval=$enable_scorefile; +fi + +{ echo "$as_me:$LINENO: checking for scoreboard file" >&5 +echo $ECHO_N "checking for scoreboard file... $ECHO_C" >&6; } +if test "x$enable_scorefile" = "xno" ; then +SCOREFILE= +elif test "x$enable_scorefile" = "xyes" ; then +SCOREFILE=$progname.scr +elif test "x$enable_scorefile" = "x" ; then +SCOREFILE=$progname.scr +else +SCOREFILE=$enable_scorefile +fi + +if test "x$SCOREFILE" != "x" ; then + +cat >>confdefs.h <<_ACEOF +#define SCOREFILE "$SCOREFILE" +_ACEOF + +{ echo "$as_me:$LINENO: result: $SCOREFILE" >&5 +echo "${ECHO_T}$SCOREFILE" >&6; } +else +{ echo "$as_me:$LINENO: result: disabled" >&5 +echo "${ECHO_T}disabled" >&6; } +fi + + + +# Check whether --enable-lockfile was given. +if test "${enable_lockfile+set}" = set; then + enableval=$enable_lockfile; +fi + +{ echo "$as_me:$LINENO: checking for scoreboard lockfile file" >&5 +echo $ECHO_N "checking for scoreboard lockfile file... $ECHO_C" >&6; } +if test "x$enable_lockfile" = "xno" ; then +LOCKFILE= +elif test "x$enable_lockfile" = "xyes" ; then +LOCKFILE=$progname.lck +elif test "x$enable_lockfile" = "x" ; then +LOCKFILE=$progname.lck +else +LOCKFILE=$enable_lockfile +fi + +if test "x$LOCKFILE" != "x" ; then + +cat >>confdefs.h <<_ACEOF +#define LOCKFILE "$LOCKFILE" +_ACEOF + +{ echo "$as_me:$LINENO: result: $LOCKFILE" >&5 +echo "${ECHO_T}$LOCKFILE" >&6; } +else +{ echo "$as_me:$LINENO: result: disabled" >&5 +echo "${ECHO_T}disabled" >&6; } +fi + + + +# Check whether --enable-wizardmode was given. +if test "${enable_wizardmode+set}" = set; then + enableval=$enable_wizardmode; +fi + +{ echo "$as_me:$LINENO: checking if wizard mode is enabled" >&5 +echo $ECHO_N "checking if wizard mode is enabled... $ECHO_C" >&6; } +if test "x$enable_wizardmode" = "xno" ; then +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +elif test "x$enable_wizardmode" = "x" ; then +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +else + +cat >>confdefs.h <<\_ACEOF +#define MASTER +_ACEOF + +if test "x$enable_wizardmode" != "xyes" ; then + +cat >>confdefs.h <<_ACEOF +#define PASSWD $enable_wizardmode +_ACEOF + +fi +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +fi + +# Check whether --enable-allscores was given. +if test "${enable_allscores+set}" = set; then + enableval=$enable_allscores; +else + enable_allscores=yes +fi + +{ echo "$as_me:$LINENO: checking if allscores is enabled" >&5 +echo $ECHO_N "checking if allscores is enabled... $ECHO_C" >&6; } +if test "x$enable_allscores" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define ALLSCORES 1 +_ACEOF + +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + +# Check whether --enable-checktime was given. +if test "${enable_checktime+set}" = set; then + enableval=$enable_checktime; +fi + +{ echo "$as_me:$LINENO: checking if checktime is enabled" >&5 +echo $ECHO_N "checking if checktime is enabled... $ECHO_C" >&6; } +if test "x$enable_checktime" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define CHECKTIME 1 +_ACEOF + +{ echo "$as_me:$LINENO: result: yes" >&5 +echo "${ECHO_T}yes" >&6; } +else +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +fi + +# Check whether --enable-maxload was given. +if test "${enable_maxload+set}" = set; then + enableval=$enable_maxload; +fi + +{ echo "$as_me:$LINENO: checking runtime execution limit (maximum system load average)" >&5 +echo $ECHO_N "checking runtime execution limit (maximum system load average)... $ECHO_C" >&6; } +if test "x$enable_maxload" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define MAXLOAD 100 +_ACEOF + +{ echo "$as_me:$LINENO: result: 100" >&5 +echo "${ECHO_T}100" >&6; } +elif test "x$enable_maxload" = "x" ; then +{ echo "$as_me:$LINENO: result: unlimited" >&5 +echo "${ECHO_T}unlimited" >&6; } +elif test "x$enable_maxload" = "xno" ; then +{ echo "$as_me:$LINENO: result: unlimited" >&5 +echo "${ECHO_T}unlimited" >&6; } +else + +cat >>confdefs.h <<_ACEOF +#define MAXLOAD $enable_maxload +_ACEOF + +{ echo "$as_me:$LINENO: result: $enable_maxload" >&5 +echo "${ECHO_T}$enable_maxload" >&6; } +fi + +# Check whether --enable-maxusers was given. +if test "${enable_maxusers+set}" = set; then + enableval=$enable_maxusers; +fi + +{ echo "$as_me:$LINENO: checking runtime execution limit (maximum online system users)" >&5 +echo $ECHO_N "checking runtime execution limit (maximum online system users)... $ECHO_C" >&6; } +if test "x$enable_maxusers" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define MAXUSERS 100 +_ACEOF + +{ echo "$as_me:$LINENO: result: 100" >&5 +echo "${ECHO_T}100" >&6; } +elif test "x$enable_maxusers" = "x" ; then +{ echo "$as_me:$LINENO: result: unlimited" >&5 +echo "${ECHO_T}unlimited" >&6; } +elif test "x$enable_maxload" = "xno" ; then +{ echo "$as_me:$LINENO: result: unlimited" >&5 +echo "${ECHO_T}unlimited" >&6; } +else + +cat >>confdefs.h <<_ACEOF +#define MAXLOAD $enable_maxusers +_ACEOF + +{ echo "$as_me:$LINENO: result: $enable_maxusers" >&5 +echo "${ECHO_T}$enable_maxusers" >&6; } +fi + +# Check whether --enable-numscores was given. +if test "${enable_numscores+set}" = set; then + enableval=$enable_numscores; +fi + +{ echo "$as_me:$LINENO: checking what the number of scores to store in scoreboard is" >&5 +echo $ECHO_N "checking what the number of scores to store in scoreboard is... $ECHO_C" >&6; } +if test "x$numscores" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define NUMSCORES 10 +_ACEOF + +{ echo "$as_me:$LINENO: result: 10" >&5 +echo "${ECHO_T}10" >&6; } +elif test "x$enable_numscores" = "x" ; then + +cat >>confdefs.h <<\_ACEOF +#define NUMSCORES 10 +_ACEOF + +{ echo "$as_me:$LINENO: result: 10" >&5 +echo "${ECHO_T}10" >&6; } +elif test "x$enable_numscores" = "xno" ; then + +cat >>confdefs.h <<\_ACEOF +#define NUMSCORES 10 +_ACEOF + +{ echo "$as_me:$LINENO: result: 10" >&5 +echo "${ECHO_T}10" >&6; } +else + +cat >>confdefs.h <<_ACEOF +#define NUMSCORES $enable_numscores +_ACEOF + +{ echo "$as_me:$LINENO: result: $enable_numscores" >&5 +echo "${ECHO_T}$enable_numscores" >&6; } +fi + +# Check whether --enable-numname was given. +if test "${enable_numname+set}" = set; then + enableval=$enable_numname; +fi + +{ echo "$as_me:$LINENO: checking word for the number of scores to store in scoreboard is" >&5 +echo $ECHO_N "checking word for the number of scores to store in scoreboard is... $ECHO_C" >&6; } +if test "x$enable_numname" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define NUMNAME "Ten" +_ACEOF + +{ echo "$as_me:$LINENO: result: Ten" >&5 +echo "${ECHO_T}Ten" >&6; } +elif test "x$enable_numname" = "x" ; then + +cat >>confdefs.h <<\_ACEOF +#define NUMNAME "Ten" +_ACEOF + +{ echo "$as_me:$LINENO: result: Ten" >&5 +echo "${ECHO_T}Ten" >&6; } +elif test "x$enable_numname" = "xno" ; then + +cat >>confdefs.h <<\_ACEOF +#define NUMNAME "Ten" +_ACEOF + +{ echo "$as_me:$LINENO: result: Ten" >&5 +echo "${ECHO_T}Ten" >&6; } +else + +cat >>confdefs.h <<_ACEOF +#define NUMNAME "$enable_numname" +_ACEOF + +{ echo "$as_me:$LINENO: result: $enable_numname" >&5 +echo "${ECHO_T}$enable_numname" >&6; } +fi + +# Check whether --enable-loadav was given. +if test "${enable_loadav+set}" = set; then + enableval=$enable_loadav; +fi + +{ echo "$as_me:$LINENO: checking whether to use program's built in load average function" >&5 +echo $ECHO_N "checking whether to use program's built in load average function... $ECHO_C" >&6; } +if test "x$enable_loadav" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define LOADAV +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define NAMELIST /vmunix +_ACEOF + +{ echo "$as_me:$LINENO: result: /vmunix" >&5 +echo "${ECHO_T}/vmunix" >&6; } +elif test "x$enable_loadav" = "x" ; then +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +elif test "x$enable_loadav" = "xno" ; then +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +else + +cat >>confdefs.h <<\_ACEOF +#define LOADAV +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define NAMELIST $enable_loadav +_ACEOF + +{ echo "$as_me:$LINENO: result: $enable_loadav" >&5 +echo "${ECHO_T}$enable_loadav" >&6; } +fi + +# Check whether --enable-ucount was given. +if test "${enable_ucount+set}" = set; then + enableval=$enable_ucount; +fi + +{ echo "$as_me:$LINENO: checking whether to use program's built in user counting function" >&5 +echo $ECHO_N "checking whether to use program's built in user counting function... $ECHO_C" >&6; } +if test "x$enable_ucount" = "xyes" ; then + +cat >>confdefs.h <<\_ACEOF +#define UCOUNT +_ACEOF + + +cat >>confdefs.h <<\_ACEOF +#define UTMP /etc/utmp +_ACEOF + +{ echo "$as_me:$LINENO: result: /etc/utmp" >&5 +echo "${ECHO_T}/etc/utmp" >&6; } +elif test "x$enable_ucount" = "x" ; then +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +elif test "x$enable_count" = "xno" ; then +{ echo "$as_me:$LINENO: result: no" >&5 +echo "${ECHO_T}no" >&6; } +else + +cat >>confdefs.h <<\_ACEOF +#define UCOUNT +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define UTMP $enable_ucount +_ACEOF + +{ echo "$as_me:$LINENO: result: $enable_ucount" >&5 +echo "${ECHO_T}$enable_ucount" >&6; } +fi + +TARGET=$target + + +{ echo "$as_me:$LINENO: checking whether to docdir is defined" >&5 +echo $ECHO_N "checking whether to docdir is defined... $ECHO_C" >&6; } +if test "x$docdir" = "x" ; then +{ echo "$as_me:$LINENO: result: docdir undefined" >&5 +echo "${ECHO_T}docdir undefined" >&6; } +docdir=\${datadir}/doc/\${PACKAGE_TARNAME} + +else +{ echo "$as_me:$LINENO: result: docdir defined" >&5 +echo "${ECHO_T}docdir defined" >&6; } +fi + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 +echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + *) $as_unset $ac_var ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes (double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \). + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { echo "$as_me:$LINENO: updating cache $cache_file" >&5 +echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 +echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" + ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: ${CONFIG_STATUS=./config.status} +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 +echo "$as_me: creating $CONFIG_STATUS" >&6;} +cat >$CONFIG_STATUS <<_ACEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +## --------------------- ## +## M4sh Initialization. ## +## --------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in + *posix*) set -o posix ;; +esac + +fi + + + + +# PATH needs CR +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + echo "#! /bin/sh" >conf$$.sh + echo "exit 0" >>conf$$.sh + chmod +x conf$$.sh + if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then + PATH_SEPARATOR=';' + else + PATH_SEPARATOR=: + fi + rm -f conf$$.sh +fi + +# Support unset when possible. +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + as_unset=unset +else + as_unset=false +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +as_nl=' +' +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +case $0 in + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break +done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + { (exit 1); exit 1; } +fi + +# Work around bugs in pre-3.0 UWIN ksh. +for as_var in ENV MAIL MAILPATH +do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +for as_var in \ + LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ + LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ + LC_TELEPHONE LC_TIME +do + if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then + eval $as_var=C; export $as_var + else + ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + fi +done + +# Required to use basename. +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + + +# Name of the executable. +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# CDPATH. +$as_unset CDPATH + + + + as_lineno_1=$LINENO + as_lineno_2=$LINENO + test "x$as_lineno_1" != "x$as_lineno_2" && + test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { + + # Create $as_me.lineno as a copy of $as_myself, but with $LINENO + # uniformly replaced by the line number. The first 'sed' inserts a + # line-number line after each line using $LINENO; the second 'sed' + # does the real work. The second script uses 'N' to pair each + # line-number line with the line containing $LINENO, and appends + # trailing '-' during substitution so that $LINENO is not a special + # case at line end. + # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the + # scripts with optimization help from Paolo Bonzini. Blame Lee + # E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 + { (exit 1); exit 1; }; } + + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in +-n*) + case `echo 'x\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + *) ECHO_C='\c';; + esac;; +*) + ECHO_N='-n';; +esac + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir +fi +echo >conf$$.file +if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' +elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p=: +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 + +# Save the log message, to keep $[0] and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by Rogue $as_me 5.4.4, which was +generated by GNU Autoconf 2.61. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +cat >>$CONFIG_STATUS <<_ACEOF +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +ac_cs_usage="\ +\`$as_me' instantiates files from templates according to the +current configuration. + +Usage: $0 [OPTIONS] [FILE]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +ac_cs_version="\\ +Rogue config.status 5.4.4 +configured by $0, generated by GNU Autoconf 2.61, + with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" + +Copyright (C) 2006 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If no file are specified by the user, then we need to provide default +# value. By we need to know if files were specified by the user. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + echo "$ac_cs_version"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + CONFIG_FILES="$CONFIG_FILES $ac_optarg" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + { echo "$as_me: error: ambiguous option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; };; + --help | --hel | -h ) + echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) { echo "$as_me: error: unrecognized option: $1 +Try \`$0 --help' for more information." >&2 + { (exit 1); exit 1; }; } ;; + + *) ac_config_targets="$ac_config_targets $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +if \$ac_cs_recheck; then + echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 + CONFIG_SHELL=$SHELL + export CONFIG_SHELL + exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "config.h") CONFIG_HEADERS="$CONFIG_HEADERS config.h" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + "rogue.6") CONFIG_FILES="$CONFIG_FILES rogue.6" ;; + "rogue.cat") CONFIG_FILES="$CONFIG_FILES rogue.cat" ;; + "rogue.doc") CONFIG_FILES="$CONFIG_FILES rogue.doc" ;; + "rogue.html") CONFIG_FILES="$CONFIG_FILES rogue.html" ;; + "rogue.me") CONFIG_FILES="$CONFIG_FILES rogue.me" ;; + + *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 +echo "$as_me: error: invalid argument: $ac_config_target" >&2;} + { (exit 1); exit 1; }; };; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap '{ (exit 1); exit 1; }' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || +{ + echo "$me: cannot create a temporary directory in ." >&2 + { (exit 1); exit 1; } +} + +# +# Set up the sed scripts for CONFIG_FILES section. +# + +# No need to generate the scripts if there are no CONFIG_FILES. +# This happens for instance when ./config.status config.h +if test -n "$CONFIG_FILES"; then + +_ACEOF + + + +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + cat >conf$$subs.sed <<_ACEOF +SHELL!$SHELL$ac_delim +PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim +PACKAGE_NAME!$PACKAGE_NAME$ac_delim +PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim +PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim +PACKAGE_STRING!$PACKAGE_STRING$ac_delim +PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim +exec_prefix!$exec_prefix$ac_delim +prefix!$prefix$ac_delim +program_transform_name!$program_transform_name$ac_delim +bindir!$bindir$ac_delim +sbindir!$sbindir$ac_delim +libexecdir!$libexecdir$ac_delim +datarootdir!$datarootdir$ac_delim +datadir!$datadir$ac_delim +sysconfdir!$sysconfdir$ac_delim +sharedstatedir!$sharedstatedir$ac_delim +localstatedir!$localstatedir$ac_delim +includedir!$includedir$ac_delim +oldincludedir!$oldincludedir$ac_delim +docdir!$docdir$ac_delim +infodir!$infodir$ac_delim +htmldir!$htmldir$ac_delim +dvidir!$dvidir$ac_delim +pdfdir!$pdfdir$ac_delim +psdir!$psdir$ac_delim +libdir!$libdir$ac_delim +localedir!$localedir$ac_delim +mandir!$mandir$ac_delim +DEFS!$DEFS$ac_delim +ECHO_C!$ECHO_C$ac_delim +ECHO_N!$ECHO_N$ac_delim +ECHO_T!$ECHO_T$ac_delim +LIBS!$LIBS$ac_delim +build_alias!$build_alias$ac_delim +host_alias!$host_alias$ac_delim +target_alias!$target_alias$ac_delim +build!$build$ac_delim +build_cpu!$build_cpu$ac_delim +build_vendor!$build_vendor$ac_delim +build_os!$build_os$ac_delim +host!$host$ac_delim +host_cpu!$host_cpu$ac_delim +host_vendor!$host_vendor$ac_delim +host_os!$host_os$ac_delim +target!$target$ac_delim +target_cpu!$target_cpu$ac_delim +target_vendor!$target_vendor$ac_delim +target_os!$target_os$ac_delim +CC!$CC$ac_delim +CFLAGS!$CFLAGS$ac_delim +LDFLAGS!$LDFLAGS$ac_delim +CPPFLAGS!$CPPFLAGS$ac_delim +ac_ct_CC!$ac_ct_CC$ac_delim +EXEEXT!$EXEEXT$ac_delim +OBJEXT!$OBJEXT$ac_delim +CPP!$CPP$ac_delim +GREP!$GREP$ac_delim +EGREP!$EGREP$ac_delim +LIBOBJS!$LIBOBJS$ac_delim +NROFF!$NROFF$ac_delim +GROFF!$GROFF$ac_delim +COLCRT!$COLCRT$ac_delim +TBL!$TBL$ac_delim +SED!$SED$ac_delim +PROGRAM!$PROGRAM$ac_delim +GROUPOWNER!$GROUPOWNER$ac_delim +SCOREFILE!$SCOREFILE$ac_delim +LOCKFILE!$LOCKFILE$ac_delim +TARGET!$TARGET$ac_delim +LTLIBOBJS!$LTLIBOBJS$ac_delim +_ACEOF + + if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 71; then + break + elif $ac_last_try; then + { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 +echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} + { (exit 1); exit 1; }; } + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` +if test -n "$ac_eof"; then + ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` + ac_eof=`expr $ac_eof + 1` +fi + +cat >>$CONFIG_STATUS <<_ACEOF +cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end +_ACEOF +sed ' +s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g +s/^/s,@/; s/!/@,|#_!!_#|/ +:n +t n +s/'"$ac_delim"'$/,g/; t +s/$/\\/; p +N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n +' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF +:end +s/|#_!!_#|//g +CEOF$ac_eof +_ACEOF + + +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF +fi # test -n "$CONFIG_FILES" + + +for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 +echo "$as_me: error: Invalid tag $ac_tag." >&2;} + { (exit 1); exit 1; }; };; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 +echo "$as_me: error: cannot find input file: $ac_f" >&2;} + { (exit 1); exit 1; }; };; + esac + ac_file_inputs="$ac_file_inputs $ac_f" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input="Generated from "`IFS=: + echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { echo "$as_me:$LINENO: creating $ac_file" >&5 +echo "$as_me: creating $ac_file" >&6;} + fi + + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin";; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { as_dir="$ac_dir" + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 +echo "$as_me: error: cannot create directory $as_dir" >&2;} + { (exit 1); exit 1; }; }; } + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= + +case `sed -n '/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p +' $ac_file_inputs` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF + sed "$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s&@configure_input@&$configure_input&;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} + + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out"; rm -f "$tmp/out";; + *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; + esac + ;; + :H) + # + # CONFIG_HEADER + # +_ACEOF + +# Transform confdefs.h into a sed script `conftest.defines', that +# substitutes the proper values into config.h.in to produce config.h. +rm -f conftest.defines conftest.tail +# First, append a space to every undef/define line, to ease matching. +echo 's/$/ /' >conftest.defines +# Then, protect against being on the right side of a sed subst, or in +# an unquoted here document, in config.status. If some macros were +# called several times there might be several #defines for the same +# symbol, which is useless. But do not sort them, since the last +# AC_DEFINE must be honored. +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where +# NAME is the cpp macro being defined, VALUE is the value it is being given. +# PARAMS is the parameter list in the macro definition--in most cases, it's +# just an empty string. +ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' +ac_dB='\\)[ (].*,\\1define\\2' +ac_dC=' ' +ac_dD=' ,' + +uniq confdefs.h | + sed -n ' + t rset + :rset + s/^[ ]*#[ ]*define[ ][ ]*// + t ok + d + :ok + s/[\\&,]/\\&/g + s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p + s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p + ' >>conftest.defines + +# Remove the space that was appended to ease matching. +# Then replace #undef with comments. This is necessary, for +# example, in the case of _POSIX_SOURCE, which is predefined and required +# on some systems where configure will not decide to define it. +# (The regexp can be short, since the line contains either #define or #undef.) +echo 's/ $// +s,^[ #]*u.*,/* & */,' >>conftest.defines + +# Break up conftest.defines: +ac_max_sed_lines=50 + +# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" +# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" +# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" +# et cetera. +ac_in='$ac_file_inputs' +ac_out='"$tmp/out1"' +ac_nxt='"$tmp/out2"' + +while : +do + # Write a here document: + cat >>$CONFIG_STATUS <<_ACEOF + # First, check the format of the line: + cat >"\$tmp/defines.sed" <<\\CEOF +/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def +/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def +b +:def +_ACEOF + sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS + echo 'CEOF + sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS + ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in + sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail + grep . conftest.tail >/dev/null || break + rm -f conftest.defines + mv conftest.tail conftest.defines +done +rm -f conftest.defines conftest.tail + +echo "ac_result=$ac_in" >>$CONFIG_STATUS +cat >>$CONFIG_STATUS <<\_ACEOF + if test x"$ac_file" != x-; then + echo "/* $configure_input */" >"$tmp/config.h" + cat "$ac_result" >>"$tmp/config.h" + if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then + { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 +echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f $ac_file + mv "$tmp/config.h" $ac_file + fi + else + echo "/* $configure_input */" + cat "$ac_result" + fi + rm -f "$tmp/out12" + ;; + + + esac + +done # for ac_tag + + +{ (exit 0); exit 0; } +_ACEOF +chmod +x $CONFIG_STATUS +ac_clean_files=$ac_clean_files_save + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || { (exit 1); exit 1; } +fi + diff --git a/src/cc/rogue/configure.ac b/src/cc/rogue/configure.ac new file mode 100644 index 000000000..15c9cc7e4 --- /dev/null +++ b/src/cc/rogue/configure.ac @@ -0,0 +1,246 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ(2.56) +AC_INIT([Rogue],[5.4.4], [yendor@rogueforge.net]) +AC_CONFIG_SRCDIR([armor.c]) +AC_CONFIG_HEADER([config.h]) +AC_CONFIG_FILES([Makefile rogue.6 rogue.cat rogue.doc rogue.html rogue.me]) +AC_CANONICAL_SYSTEM([]) + +# Checks for programs. +AC_PROG_CC + +# Checks for libraries. + +# Checks for header files. +AC_HEADER_STDC +AC_CHECK_HEADERS([arpa/inet.h sys/utsname.h pwd.h fcntl.h limits.h nlist.h stdlib.h string.h sys/ioctl.h termios.h unistd.h utmp.h term.h ncurses/term.h process.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_CONST +AC_TYPE_UID_T +AC_TYPE_SIZE_T +AC_STRUCT_TM +MP_WITH_CURSES +# Checks for library functions. +AC_FUNC_FORK +AC_PROG_GCC_TRADITIONAL +AC_FUNC_LSTAT +AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK +AC_TYPE_SIGNAL +AC_FUNC_STAT +AC_FUNC_VPRINTF +AC_CHECK_FUNCS([erasechar killchar alarm getpass memset setenv strchr nlist _spawnl spawnl getpwuid loadav getloadavg strerror setresgid setregid setgid setresuid setreuid setuid getuid getgid]) + +AC_CHECK_PROG([NROFF], [nroff], [nroff],) +AC_CHECK_PROG([GROFF], [groff], [groff],) +AC_CHECK_PROG([COLCRT], [colcrt], [colcrt],) +AC_CHECK_PROG([TBL], [tbl], [tbl],) +AC_CHECK_PROG([SED], [sed], [sed],) + +AC_ARG_WITH(program-name, AC_HELP_STRING([--with-program-name=NAME],[alternate executable name]),[progname="$withval" ], [progname="rogue"] ) +PROGRAM=$progname +AC_SUBST(PROGRAM) + +AC_ARG_ENABLE(setgid, AC_HELP_STRING([--enable-setgid=NAME],[install executable as setgid with group ownership of NAME @<:@default=no@:>@])],[],[]) +AC_MSG_CHECKING([if using setgid execute bit]) +if test "x$enable_setgid" = "xno" ; then +GROUPOWNER= +elif test "x$enable_setgid" = "xyes" ; then +GROUPOWNER=games +elif test "x$enable_setgid" = "x" ; then +GROUPOWNER= +else +GROUPOWNER=$enable_setgid +fi + +if test "x$GROUPOWNER" != "x" ; then +AC_DEFINE_UNQUOTED([GROUPOWNER],[$GROUPOWNER], [Define to group owner of setgid executable]) +AC_MSG_RESULT([$GROUPOWNER]) +else +AC_MSG_RESULT([no]) +fi + +AC_SUBST(GROUPOWNER) + +AC_ARG_ENABLE([scorefile],[AC_HELP_STRING([--enable-scorefile=SCOREFILE], [enable scoreboard with given filename])],[],[]) +AC_MSG_CHECKING([for scoreboard file]) +if test "x$enable_scorefile" = "xno" ; then +SCOREFILE= +elif test "x$enable_scorefile" = "xyes" ; then +SCOREFILE=$progname.scr +elif test "x$enable_scorefile" = "x" ; then +SCOREFILE=$progname.scr +else +SCOREFILE=$enable_scorefile +fi + +if test "x$SCOREFILE" != "x" ; then +AC_DEFINE_UNQUOTED([SCOREFILE], ["$SCOREFILE"], [Define to file to use for scoreboard]) +AC_MSG_RESULT([$SCOREFILE]) +else +AC_MSG_RESULT([disabled]) +fi + +AC_SUBST(SCOREFILE) + +AC_ARG_ENABLE([lockfile],[AC_HELP_STRING([--enable-lockfile=LOCKFILE], [enable scoreboard lockfile with given filename])],[],[]) +AC_MSG_CHECKING([for scoreboard lockfile file]) +if test "x$enable_lockfile" = "xno" ; then +LOCKFILE= +elif test "x$enable_lockfile" = "xyes" ; then +LOCKFILE=$progname.lck +elif test "x$enable_lockfile" = "x" ; then +LOCKFILE=$progname.lck +else +LOCKFILE=$enable_lockfile +fi + +if test "x$LOCKFILE" != "x" ; then +AC_DEFINE_UNQUOTED([LOCKFILE], ["$LOCKFILE"], [Define to file to use for scoreboard lockfile]) +AC_MSG_RESULT([$LOCKFILE]) +else +AC_MSG_RESULT([disabled]) +fi + +AC_SUBST(LOCKFILE) + +AC_ARG_ENABLE([wizardmode],[AC_HELP_STRING([--enable-wizardmode], [enable availability of wizard mode @<:@default=no@:>@])],[],[]) +AC_MSG_CHECKING([if wizard mode is enabled]) +if test "x$enable_wizardmode" = "xno" ; then +AC_MSG_RESULT([no]) +elif test "x$enable_wizardmode" = "x" ; then +AC_MSG_RESULT([no]) +else +AC_DEFINE([MASTER], [], [Define to include wizard mode]) +if test "x$enable_wizardmode" != "xyes" ; then +AC_DEFINE_UNQUOTED([PASSWD],[$enable_wizardmode], [Define crypt(3) wizard mode password]) +fi +AC_MSG_RESULT([yes]) +fi + +AC_ARG_ENABLE([allscores],[AC_HELP_STRING([--enable-allscores], [enable scoreboard to show top scores, not just top players @<:@default=yes@:>@])],[],[enable_allscores=yes]) +AC_MSG_CHECKING([if allscores is enabled]) +if test "x$enable_allscores" = "xyes" ; then +AC_DEFINE([ALLSCORES], [1], [Define if scorefile is top scores, not top players]) +AC_MSG_RESULT([yes]) +else +AC_MSG_RESULT([no]) +fi + +AC_ARG_ENABLE([checktime],[AC_HELP_STRING([--enable-checktime], [enable checktime @<:@default=no@:>@])],[],[]) +AC_MSG_CHECKING([if checktime is enabled]) +if test "x$enable_checktime" = "xyes" ; then +AC_DEFINE([CHECKTIME], [1], [Define if checktime feature should be enabled]) +AC_MSG_RESULT([yes]) +else +AC_MSG_RESULT([no]) +fi + +AC_ARG_ENABLE([maxload],[AC_HELP_STRING([--enable-maxload], [enable maxload @<:@default=no@:>@])],[],[]) +AC_MSG_CHECKING([runtime execution limit (maximum system load average)]) +if test "x$enable_maxload" = "xyes" ; then +AC_DEFINE([MAXLOAD], [100], [Define if maxload feature should be enabled]) +AC_MSG_RESULT([100]) +elif test "x$enable_maxload" = "x" ; then +AC_MSG_RESULT([unlimited]) +elif test "x$enable_maxload" = "xno" ; then +AC_MSG_RESULT([unlimited]) +else +AC_DEFINE_UNQUOTED([MAXLOAD], [$enable_maxload], [Define if maxload feature should be enabled]) +AC_MSG_RESULT([$enable_maxload]) +fi + +AC_ARG_ENABLE([maxusers],[AC_HELP_STRING([--enable-maxusers], [enable maxuser @<:@default=no@:>@])],[],[]) +AC_MSG_CHECKING([runtime execution limit (maximum online system users)]) +if test "x$enable_maxusers" = "xyes" ; then +AC_DEFINE([MAXUSERS], [100], [Define if maxusers feature should be enabled]) +AC_MSG_RESULT([100]) +elif test "x$enable_maxusers" = "x" ; then +AC_MSG_RESULT([unlimited]) +elif test "x$enable_maxload" = "xno" ; then +AC_MSG_RESULT([unlimited]) +else +AC_DEFINE_UNQUOTED([MAXLOAD], [$enable_maxusers], [Define if maxusers feature should be enabled]) +AC_MSG_RESULT([$enable_maxusers]) +fi + +AC_ARG_ENABLE([numscores],[AC_HELP_STRING([--enable-numscores], [number of scores to store in scoreboard @<:@default=10@:>@])],[],[]) +AC_MSG_CHECKING([what the number of scores to store in scoreboard is]) +if test "x$numscores" = "xyes" ; then +AC_DEFINE([NUMSCORES], [10], [number of scores to store in scoreboard]) +AC_MSG_RESULT([10]) +elif test "x$enable_numscores" = "x" ; then +AC_DEFINE([NUMSCORES], [10], [number of scores to store in scoreboard]) +AC_MSG_RESULT([10]) +elif test "x$enable_numscores" = "xno" ; then +AC_DEFINE([NUMSCORES], [10], [number of scores to store in scoreboard]) +AC_MSG_RESULT([10]) +else +AC_DEFINE_UNQUOTED([NUMSCORES], [$enable_numscores], [number of scores to store in scoreboard]) +AC_MSG_RESULT([$enable_numscores]) +fi + +AC_ARG_ENABLE([numname],[AC_HELP_STRING([--enable-numname], [word for number of scores to store in scoreboard @<:@default=Ten@:>@])],[],[]) +AC_MSG_CHECKING([word for the number of scores to store in scoreboard is]) +if test "x$enable_numname" = "xyes" ; then +AC_DEFINE([NUMNAME], ["Ten"], [word for the number of scores to store in scoreboard]) +AC_MSG_RESULT([Ten]) +elif test "x$enable_numname" = "x" ; then +AC_DEFINE([NUMNAME], ["Ten"], [word for the number of scores to store in scoreboard]) +AC_MSG_RESULT([Ten]) +elif test "x$enable_numname" = "xno" ; then +AC_DEFINE([NUMNAME], ["Ten"], [word for the number of scores to store in scoreboard]) +AC_MSG_RESULT([Ten]) +else +AC_DEFINE_UNQUOTED([NUMNAME], ["$enable_numname"], [word for the number of scores to store in scoreboard]) +AC_MSG_RESULT([$enable_numname]) +fi + +AC_ARG_ENABLE([loadav],[AC_HELP_STRING([--enable-loadav=NAMELIST], [use program's load average function (unlikely to work) @<:@default=no@:>@])],[],[]) +AC_MSG_CHECKING([whether to use program's built in load average function]) +if test "x$enable_loadav" = "xyes" ; then +AC_DEFINE([LOADAV], [], [define if we should use program's load average function instead of system]) +AC_DEFINE([NAMELIST], [/vmunix], [kernel file to pass to nlist() when reading load average (unlikely to work)]) +AC_MSG_RESULT([/vmunix]) +elif test "x$enable_loadav" = "x" ; then +AC_MSG_RESULT([no]) +elif test "x$enable_loadav" = "xno" ; then +AC_MSG_RESULT([no]) +else +AC_DEFINE([LOADAV], [], [define if we should use program's load average function instead of system]) +AC_DEFINE_UNQUOTED([NAMELIST], [$enable_loadav], [kernel file to pass to nlist() when reading load average (unlikely to work)]) +AC_MSG_RESULT([$enable_loadav]) +fi + +AC_ARG_ENABLE([ucount],[AC_HELP_STRING([--enable-ucount=UTMPFILE], [use program's own function to count users (unlikely to work) @<:@default=no@:>@])],[],[]) +AC_MSG_CHECKING([whether to use program's built in user counting function]) +if test "x$enable_ucount" = "xyes" ; then +AC_DEFINE([UCOUNT], [], [define if we should use program's user counting function instead of system's]) +AC_DEFINE([UTMP], [/etc/utmp], [utmp like file to pass to ucount() when counting online users (unlikely to work)]) +AC_MSG_RESULT([/etc/utmp]) +elif test "x$enable_ucount" = "x" ; then +AC_MSG_RESULT([no]) +elif test "x$enable_count" = "xno" ; then +AC_MSG_RESULT([no]) +else +AC_DEFINE([UCOUNT], [], [define if we should use program's user counting function instead of system's]) +AC_DEFINE_UNQUOTED([UTMP], [$enable_ucount], [utmp like file to pass to ucount() when counting online users (unlikely to work)]) +AC_MSG_RESULT([$enable_ucount]) +fi + +TARGET=$target +AC_SUBST(TARGET) + +AC_MSG_CHECKING([whether to docdir is defined]) +if test "x$docdir" = "x" ; then +AC_MSG_RESULT([docdir undefined]) +docdir=\${datadir}/doc/\${PACKAGE_TARNAME} +AC_SUBST(docdir) +else +AC_MSG_RESULT([docdir defined]) +fi + +AC_OUTPUT diff --git a/src/cc/rogue/cursesd.c b/src/cc/rogue/cursesd.c new file mode 100644 index 000000000..6c295fc94 --- /dev/null +++ b/src/cc/rogue/cursesd.c @@ -0,0 +1,432 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include "cursesd.h" + +static int32_t endwinflag; +WINDOW *stdscr,*curscr; +int32_t ESCDELAY; + +WINDOW *newwin(int32_t nlines,int32_t ncols,int32_t begin_y,int32_t begin_x) +{ + WINDOW *scr = 0; + if ( nlines == LINES && ncols == COLS && begin_y == 0 && begin_x == 0 ) + scr = (WINDOW *)calloc(1,sizeof(*stdscr)); + curscr = scr; + return(scr); +} + +WINDOW *initscr() +{ + if ( stdscr == 0 ) + stdscr = newwin(LINES,COLS,0,0); + return(stdscr); +} + +int32_t delwin(WINDOW *win) +{ + free(win); + return(0); +} + +int32_t wmove(WINDOW *win, int32_t y, int32_t x) +{ + win->y = y; + win->x = x; + return(0); +} + +int32_t move(int32_t y, int32_t x) +{ + return(wmove(stdscr,y,x)); +} + +int32_t werase(WINDOW *win) +{ + memset(win->screen,' ',sizeof(win->screen)); + return(0); +} + +int32_t wclear(WINDOW *win) +{ + werase(win); + clearok(win,TRUE); + return(0); +} + +int32_t wclrtoeol(WINDOW *win) +{ + if ( win->x < COLS-1 ) + memset(&win->screen[win->y][win->x],' ',COLS - win->x); + return(0); +} + +int32_t wclrtobot(WINDOW *win) +{ + wclrtoeol(win); + if ( win->y < LINES-1 ) + memset(&win->screen[win->y+1][0],' ',COLS); + return(0); +} + +int32_t erase(void) +{ + return(werase(stdscr)); +} + +int32_t clear(void) +{ + return(wclear(stdscr)); +} + +int32_t clrtobot(void) +{ + return(wclrtobot(stdscr)); +} + +int32_t clrtoeol(void) +{ + return(wclrtoeol(stdscr)); +} + +int32_t waddch(WINDOW *win, chtype ch) +{ + int32_t i; + if ( ch == '\t' ) + { + for (i=0; i<8; i++) + { + if ( win->x >= COLS-1 ) + break; + win->x++; + if ( (win->x & 7) == 0 ) + break; + } + } + else if ( ch == '\n' ) + { + wclrtoeol(win); + win->x = 0; + win->y++; + if ( win->y >= LINES ) + win->y = 0; + } + else if ( ch == '\b' ) + { + if ( win->x > 0 ) + win->x--; + } + else + { + win->screen[win->y][win->x++] = ch; + if ( win->x >= COLS ) + { + win->x = 0; + win->y++; + if ( win->y >= LINES ) + win->y = 0; + } + } + return(0); +} + +int32_t mvwaddch(WINDOW *win, int32_t y, int32_t x, chtype ch) +{ + win->y = y; + win->x = x; + return(waddch(win,ch)); +} + +int32_t addch(chtype ch) +{ + return(waddch(stdscr,ch)); +} + +int32_t mvaddch(int32_t y, int32_t x, chtype ch) +{ + return(mvwaddch(stdscr,y,x,ch)); +} + +int32_t waddstr(WINDOW *win, const char *str) +{ + int32_t i; + //fprintf(stderr,"%s\n",str); + for (i=0; str[i]!=0; i++) + waddch(win,str[i]); + return(0); +} + +int32_t waddnstr(WINDOW *win, const char *str, int32_t n) +{ + int32_t i; + for (i=0; str[i]!=0 && iy = y; + win->x = x; + return(waddstr(win,str)); +} + +int32_t mvwaddnstr(WINDOW *win, int32_t y, int32_t x, const char *str, int32_t n) +{ + win->y = y; + win->x = x; + return(waddnstr(win,str,n)); +} + +int32_t addstr(const char *str) +{ + return(waddstr(stdscr,str)); +} + +int32_t addnstr(const char *str, int32_t n) +{ + return(waddnstr(stdscr,str,n)); +} + +int32_t mvaddstr(int32_t y, int32_t x, const char *str) +{ + stdscr->y = y; + stdscr->x = x; + return(waddstr(stdscr,str)); +} + +int32_t mvaddnstr(int32_t y, int32_t x, const char *str, int32_t n) +{ + stdscr->y = y; + stdscr->x = x; + return(waddnstr(stdscr,str,n)); +} + +int32_t printw(char *fmt,...) +{ + char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable + va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt + ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf + va_end(myargs); // Clean up the va_list + return(addstr(str)); +} + +int32_t wprintw(WINDOW *win,char *fmt,...) +{ + char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable + va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt + ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf + va_end(myargs); // Clean up the va_list + return(waddstr(win,str)); +} + +int32_t mvprintw(int32_t y,int32_t x,char *fmt,...) +{ + char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable + va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt + ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf + va_end(myargs); // Clean up the va_list + stdscr->y = y; + stdscr->x = x; + return(addstr(str)); +} + +int32_t mvwprintw(WINDOW *win,int32_t y,int32_t x,char *fmt,...) +{ + char str[512]; int32_t ret; va_list myargs; // Declare a va_list type variable + va_start(myargs,fmt); // Initialise the va_list variable with the ... after fmt + ret = vsprintf(str,fmt,myargs); // Forward the '...' to vsprintf + va_end(myargs); // Clean up the va_list + win->y = y; + win->x = x; + return(waddstr(win,str)); +} + +chtype winch(WINDOW *win) +{ + return(win->screen[win->y][win->x]); +} + +chtype inch(void) +{ + return(winch(stdscr)); +} + +chtype mvwinch(WINDOW *win, int32_t y, int32_t x) +{ + win->y = y; + win->x = x; + return(win->screen[win->y][win->x]); +} + +chtype mvinch(int32_t y, int32_t x) +{ + return(mvwinch(stdscr,y,x)); +} + +int32_t mvcur(int32_t oldrow, int32_t oldcol, int32_t newrow, int32_t newcol) +{ + stdscr->y = newrow; + stdscr->x = newcol; + return(0); +} + +int32_t endwin(void) +{ + if ( stdscr != 0 ) + free(stdscr), stdscr = 0; + endwinflag = 1; + return(0); +} + +int32_t isendwin(void) +{ + return(endwinflag); +} + +int32_t refresh(void) +{ + endwinflag = 0; + return(wrefresh(stdscr)); +} + +int32_t redrawwin(WINDOW *win) +{ + return(wrefresh(win)); +} + +int32_t wredrawln(WINDOW *win, int32_t beg_line, int32_t num_lines) +{ + return(wrefresh(win)); +} + +// functions with no data side effect +#ifdef they_are_macros +int32_t wrefresh(WINDOW *win) +{ + return(0); +} + +int32_t wnoutrefresh(WINDOW *win) +{ + return(0); +} + +int32_t doupdate(void) +{ + return(0); +} + +int32_t touchwin(WINDOW *win) +{ + return(0); +} + +int32_t standout(void) +{ + return(0); +} + +int32_t standend(void) +{ + return(0); +} + +int32_t raw(void) +{ + return(0); +} + +int32_t keypad(WINDOW *win, bool bf) +{ + return(0); +} + +int32_t noecho(void) +{ + return(0); +} + +int32_t flushinp(void) +{ + return(0); +} + +int32_t clearok(WINDOW *win, bool bf) +{ + return(0); +} + +int32_t idlok(WINDOW *win, bool bf) +{ + return(0); +} + +int32_t leaveok(WINDOW *win, bool bf) +{ + return(0); +} +#endif + +int32_t mvwin(WINDOW *win, int32_t y, int32_t x) // stub +{ + fprintf(stderr,"unexpected call to mvwin\n"); + return(0); +} + +WINDOW *subwin(WINDOW *orig, int32_t nlines, int32_t ncols, int32_t begin_y, int32_t begin_x) +{ + fprintf(stderr,"unexpected and unsupported call to subwin\n"); + return(0); +} + +char erasechar(void) +{ + fprintf(stderr,"unexpected and unsupported call to erasechar\n"); + return(8); +} + +char killchar(void) +{ + fprintf(stderr,"unexpected and unsupported call to erasechar\n"); + return(3); +} + +int32_t wgetnstr(WINDOW *win, char *str, int32_t n) // stub +{ + fprintf(stderr,"unexpected and unsupported call to mvgetnstr\n"); + return(0); +} + +#ifndef __MINGW32__ +int32_t getch(void) +{ + fprintf(stderr,"unexpected and unsupported call to getch\n"); + return(0); +} +#endif + +int32_t md_readchar(void) +{ + fprintf(stderr,"unexpected and unsupported call to md_readchar\n"); + return(0); +} + +char *unctrl(char c) +{ + static char ctrlstr[5]; + sprintf(ctrlstr,"^%%%02x",c); + return(ctrlstr); +} diff --git a/src/cc/rogue/cursesd.h b/src/cc/rogue/cursesd.h new file mode 100644 index 000000000..7dd83d435 --- /dev/null +++ b/src/cc/rogue/cursesd.h @@ -0,0 +1,198 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef H_CURSESD_H +#define H_CURSESD_H + +#define KEY_OFFSET 0x100 +#define KEY_DOWN (KEY_OFFSET + 0x02) /* Down arrow key */ +#define KEY_UP (KEY_OFFSET + 0x03) /* Up arrow key */ +#define KEY_LEFT (KEY_OFFSET + 0x04) /* Left arrow key */ +#define KEY_RIGHT (KEY_OFFSET + 0x05) /* Right arrow key */ + + +#define COLOR_BLACK 0 + +#ifdef PDC_RGB /* RGB */ +# define COLOR_RED 1 +# define COLOR_GREEN 2 +# define COLOR_BLUE 4 +#else /* BGR */ +# define COLOR_BLUE 1 +# define COLOR_GREEN 2 +# define COLOR_RED 4 +#endif + +#define COLOR_CYAN (COLOR_BLUE | COLOR_GREEN) +#define COLOR_MAGENTA (COLOR_RED | COLOR_BLUE) +#define COLOR_YELLOW (COLOR_RED | COLOR_GREEN) + +#define COLOR_WHITE 7 + +#define LINES 24 +#define COLS 80 + +#include +#include +#include +#include +#include +#include +#include /* we need va_list */ +#include /* we want wchar_t */ +#include +#include + +#include +#include +#include +#include +#include + +#define ERR (-1) + +struct cursesd_info +{ + uint8_t screen[LINES][COLS]; + int32_t x,y; +}; +typedef struct cursesd_info WINDOW; +extern WINDOW *stdscr,*curscr; +extern int32_t ESCDELAY; +typedef char chtype; + +#ifndef __MINGW32__ +int32_t getch(void); // stub +#endif + +int32_t md_readchar(void); // stub + +WINDOW *initscr(void); +int32_t endwin(void); +int32_t isendwin(void); +//SCREEN *newterm(const char *type, FILE *outfd, FILE *infd); +//SCREEN *set_term(SCREEN *new); +//void delscreen(SCREEN* sp); + +int32_t refresh(void); +int32_t wrefresh(WINDOW *win); +//int32_t wnoutrefresh(WINDOW *win); +//int32_t doupdate(void); +int32_t redrawwin(WINDOW *win); +int32_t wredrawln(WINDOW *win, int32_t beg_line, int32_t num_lines); + +int32_t erase(void); +int32_t werase(WINDOW *win); +int32_t clear(void); +int32_t wclear(WINDOW *win); +int32_t clrtobot(void); +int32_t wclrtobot(WINDOW *win); +int32_t clrtoeol(void); +int32_t wclrtoeol(WINDOW *win); + +int32_t standout(void); +int32_t standend(void); +int32_t raw(void); +int32_t noecho(void); +int32_t flushinp(void); +int32_t keypad(WINDOW *win, bool bf); + +int32_t clearok(WINDOW *win, bool bf); +int32_t idlok(WINDOW *win, bool bf); +int32_t leaveok(WINDOW *win, bool bf); + +WINDOW *newwin(int32_t nlines,int32_t ncols,int32_t begin_y,int32_t begin_x); // only LINES,COLS,0,0 +int32_t delwin(WINDOW *win); +int32_t touchwin(WINDOW *win); // stub +WINDOW *subwin(WINDOW *orig, int32_t nlines, int32_t ncols, int32_t begin_y, int32_t begin_x); // stub +int32_t mvwin(WINDOW *win, int32_t y, int32_t x); // stub +int32_t mvcur(int32_t oldrow, int32_t oldcol, int32_t newrow, int32_t newcol); + +char erasechar(void); // stub +char killchar(void); // stub + +int32_t move(int32_t y, int32_t x); +int32_t wmove(WINDOW *win, int32_t y, int32_t x); + +chtype inch(void); +chtype winch(WINDOW *win); +chtype mvinch(int32_t y, int32_t x); +chtype mvwinch(WINDOW *win, int32_t y, int32_t x); + +int32_t addch(chtype ch); +int32_t waddch(WINDOW *win, chtype ch); +int32_t mvaddch(int32_t y, int32_t x, chtype ch); +int32_t mvwaddch(WINDOW *win, int32_t y, int32_t x, chtype ch); + +int32_t addstr(const char *str); +int32_t addnstr(const char *str, int32_t n); +int32_t waddstr(WINDOW *win, const char *str); +int32_t waddnstr(WINDOW *win, const char *str, int32_t n); +int32_t mvaddstr(int32_t y, int32_t x, const char *str); +int32_t mvaddnstr(int32_t y, int32_t x, const char *str, int32_t n); +int32_t mvwaddstr(WINDOW *win, int32_t y, int32_t x, const char *str); +int32_t mvwaddnstr(WINDOW *win, int32_t y, int32_t x, const char *str, int32_t n); + +int32_t wgetnstr(WINDOW *win, char *str, int32_t n); // stub +int32_t printw(char *fmt,...); +int32_t wprintw(WINDOW *win,char *fmt,...); +int32_t mvprintw(int32_t y,int32_t x,char *fmt,...); +int32_t mvwprintw(WINDOW *win,int32_t y,int32_t x,char *fmt,...); + +char *unctrl(char c); + +#define A_CHARTEXT 0xff +#define baudrate() 9600 +#define getmaxx(a) COLS +#define getmaxy(a) LINES +#define getyx(win,_argfory,_argforx) _argfory = win->y, _argforx = win->x + +// functions with only visible effects +#define wrefresh(win) 0 +#define wnoutrefresh(win) 0 +#define doupdate() 0 +#define touchwin(win) 0 +#define standout() 0 +#define standend() 0 +#define raw() 0 +#define keypad(win,bf) 0 +#define noecho() 0 +#define flushinp() 0 +#define clearok(win,bf) 0 +#define idlok(win,bf) 0 +#define leaveok(win,bf) 0 +#define halfdelay(x) 0 +#define nocbreak() 0 +#define cbreak() 0 +#define curs_set(x) 0 + +// for tetris +#define init_pair(a,b,c) 0 +#define start_color() 0 +#define box(a,b,c) 0 +#define A_REVERSE 0 +#define COLOR_PAIR(a) 0 +#define timeout(x) 0 +// end for tetris + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif + +#endif + diff --git a/src/cc/rogue/daemon.c b/src/cc/rogue/daemon.c new file mode 100644 index 000000000..81bdd0a73 --- /dev/null +++ b/src/cc/rogue/daemon.c @@ -0,0 +1,216 @@ +/* + * Contains functions for dealing with things that happen in the + * future. + * + * @(#)daemon.c 4.7 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +#include "rogue.h" + +#define EMPTY 0 +#define DAEMON -1 +#define MAXDAEMONS 20 + +#define _X_ { EMPTY } + +struct delayed_action d_list[MAXDAEMONS] = { + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, + _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, _X_, +}; + +/* + * d_slot: + * Find an empty slot in the daemon/fuse list + */ +struct delayed_action * +d_slot() +{ + register struct delayed_action *dev; + + for (dev = d_list; dev <= &d_list[MAXDAEMONS-1]; dev++) + if (dev->d_type == EMPTY) + return dev; +#ifdef MASTER + debug("Ran out of fuse slots"); +#endif + return NULL; +} + +/* + * find_slot: + * Find a particular slot in the table + */ +struct delayed_action * +find_slot(void (*func)(struct rogue_state *rs,int)) +{ + register struct delayed_action *dev; + + for (dev = d_list; dev <= &d_list[MAXDAEMONS-1]; dev++) + if (dev->d_type != EMPTY && func == dev->d_func) + return dev; + return NULL; +} + +/* + * start_daemon: + * Start a daemon, takes a function. + */ +void +start_daemon(void (*func)(struct rogue_state *rs,int), int arg, int type) +{ + register struct delayed_action *dev; + + dev = d_slot(); + dev->d_type = type; + dev->d_func = func; + dev->d_arg = arg; + dev->d_time = DAEMON; +} + +/* + * kill_daemon: + * Remove a daemon from the list + */ +void +kill_daemon(void (*func)(struct rogue_state *rs,int)) +{ + register struct delayed_action *dev; + + if ((dev = find_slot(func)) == NULL) + return; + /* + * Take it out of the list + */ + dev->d_type = EMPTY; +} + +/* + * do_daemons: + * Run all the daemons that are active with the current flag, + * passing the argument to the function. + */ +void +do_daemons(struct rogue_state *rs,int flag) +{ + register struct delayed_action *dev; + + /* + * Loop through the devil list + */ + for (dev = d_list; dev <= &d_list[MAXDAEMONS-1]; dev++) + /* + * Executing each one, giving it the proper arguments + */ + if (dev->d_type == flag && dev->d_time == DAEMON) + (*dev->d_func)(rs,dev->d_arg); +} + +/* + * fuse: + * Start a fuse to go off in a certain number of turns + */ +void +fuse(void (*func)(struct rogue_state *rs,int), int arg, int time, int type) +{ + register struct delayed_action *wire; + + wire = d_slot(); + wire->d_type = type; + wire->d_func = func; + wire->d_arg = arg; + wire->d_time = time; +} + +/* + * lengthen: + * Increase the time until a fuse goes off + */ +void +lengthen(void (*func)(struct rogue_state *rs,int), int xtime) +{ + register struct delayed_action *wire; + + if ((wire = find_slot(func)) == NULL) + return; + wire->d_time += xtime; +} + +/* + * extinguish: + * Put out a fuse + */ +void +extinguish(void (*func)(struct rogue_state *rs,int)) +{ + register struct delayed_action *wire; + + if ((wire = find_slot(func)) == NULL) + return; + wire->d_type = EMPTY; +} + +/* + * do_fuses: + * Decrement counters and start needed fuses + */ + +/*char *actionfunc_str(char *str,void *ptr) +{ + if ( ptr == (void *)runners ) + strcpy(str,"runners"); + else if ( ptr == (void *)doctor ) + strcpy(str,"doctor"); + else if ( ptr == (void *)stomach ) + strcpy(str,"stomach"); + else if ( ptr == (void *)nohaste ) + strcpy(str,"nohaste"); + else if ( ptr == (void *)unconfuse ) + strcpy(str,"unconfuse"); + else if ( ptr == (void *)swander ) + strcpy(str,"swander"); + else if ( ptr == (void *)come_down ) + strcpy(str,"come_down"); + else if ( ptr == (void *)unsee ) + strcpy(str,"unsee"); + else if ( ptr == (void *)sight ) + strcpy(str,"sight"); + else if ( ptr == (void *)land ) + strcpy(str,"land"); + else if ( ptr == (void *)rollwand ) + strcpy(str,"rollwand"); + else if ( ptr == (void *)visuals ) + strcpy(str,"visuals"); + else if ( ptr == (void *)turn_see ) + strcpy(str,"turn_see"); + else strcpy(str,"no match"); + return(str); +}*/ + +void +do_fuses(struct rogue_state *rs,int flag) +{ + register struct delayed_action *wire; char str[64]; + + /* + * Step though the list + */ + for (wire = d_list; wire <= &d_list[MAXDAEMONS-1]; wire++) + /* + * Decrementing counters and starting things we want. We also need + * to remove the fuse from the list once it has gone off. + */ + if (flag == wire->d_type && wire->d_time > 0 && --wire->d_time == 0) + { + //if ( fp != 0 ) + // fprintf(fp,"t.%d %d %s, ",wire->d_type,wire->d_time,actionfunc_str(str,wire->d_func)); + wire->d_type = EMPTY; + (*wire->d_func)(rs,wire->d_arg); + } +} diff --git a/src/cc/rogue/daemons.c b/src/cc/rogue/daemons.c new file mode 100644 index 000000000..c9c16448d --- /dev/null +++ b/src/cc/rogue/daemons.c @@ -0,0 +1,361 @@ +/* + * All the daemon and fuse functions are in here + * + * @(#)daemons.c 4.24 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +#include "rogue.h" + +/* + * doctor: + * A healing daemon that restors hit points after rest + */ +void +doctor(struct rogue_state *rs,int arg) +{ + register int lv, ohp; + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"doctor\n"); + + lv = pstats.s_lvl; + ohp = pstats.s_hpt; + quiet++; + if (lv < 8) + { + if (quiet + (lv << 1) > 20) + pstats.s_hpt++; + } + else + if (quiet >= 3) + pstats.s_hpt += rnd(lv - 7) + 1; + if (ISRING(LEFT, R_REGEN)) + pstats.s_hpt++; + if (ISRING(RIGHT, R_REGEN)) + pstats.s_hpt++; + if (ohp != pstats.s_hpt) + { + if (pstats.s_hpt > max_hp) + pstats.s_hpt = max_hp; + quiet = 0; + } +} + +/* + * Swander: + * Called when it is time to start rolling for wandering monsters + */ +void +swander(struct rogue_state *rs,int arg) +{ + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"swander\n"); + start_daemon(rollwand, 0, BEFORE); +} + +/* + * rollwand: + * Called to roll to see if a wandering monster starts up + */ +int between = 0; +void +rollwand(struct rogue_state *rs,int arg) +{ + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"rollwand\n"); + if (++between >= 4) + { + if (roll(1, 6) == 4) + { + wanderer(rs); + kill_daemon(rollwand); + fuse(swander, 0, WANDERTIME, BEFORE); + } + between = 0; + } +} + +/* + * unconfuse: + * Release the poor player from his confusion + */ +void +unconfuse(struct rogue_state *rs,int arg) +{ + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"unconfuse\n"); + player.t_flags &= ~ISHUH; + msg(rs,"you feel less %s now", choose_str("trippy", "confused")); +} + +/* + * unsee: + * Turn off the ability to see invisible + */ +void +unsee(struct rogue_state *rs,int arg) +{ + register THING *th; + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"unsee\n"); + + for (th = mlist; th != NULL; th = next(th)) + if (on(*th, ISINVIS) && see_monst(th)) + mvaddch(th->t_pos.y, th->t_pos.x, th->t_oldch); + player.t_flags &= ~CANSEE; +} + +/* + * sight: + * He gets his sight back + */ +void +sight(struct rogue_state *rs,int arg) +{ + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"sight\n"); + if (on(player, ISBLIND)) + { + extinguish(sight); + player.t_flags &= ~ISBLIND; + if (!(proom->r_flags & ISGONE)) + enter_room(rs,&hero); + msg(rs,choose_str("far out! Everything is all cosmic again", + "the veil of darkness lifts")); + } +} + +/* + * nohaste: + * End the hasting + */ +void +nohaste(struct rogue_state *rs,int arg) +{ + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"nohaste\n"); + player.t_flags &= ~ISHASTE; + msg(rs,"you feel yourself slowing down"); +} + +/* + * stomach: + * Digest the hero's food + */ +void +stomach(struct rogue_state *rs,int arg) +{ + register int oldfood; + int orig_hungry = hungry_state; + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"stomach\n"); + + if (food_left <= 0) + { + if (food_left-- < -STARVETIME) + death(rs,'s'); + /* + * the hero is fainting + */ + if (no_command || rnd(5) != 0) + return; + no_command += rnd(8) + 4; + hungry_state = 3; + if (!terse) + addmsg(rs,choose_str("the munchies overpower your motor capabilities. ", + "you feel too weak from lack of food. ")); + msg(rs,choose_str("You freak out", "You faint")); + } + else + { + oldfood = food_left; + food_left -= ring_eat(LEFT) + ring_eat(RIGHT) + 1 - amulet; + + if (food_left < MORETIME && oldfood >= MORETIME) + { + hungry_state = 2; + msg(rs,choose_str("the munchies are interfering with your motor capabilites", + "you are starting to feel weak")); + } + else if (food_left < 2 * MORETIME && oldfood >= 2 * MORETIME) + { + hungry_state = 1; + if (terse) + msg(rs,choose_str("getting the munchies", "getting hungry")); + else + msg(rs,choose_str("you are getting the munchies", + "you are starting to get hungry")); + } + } + if (hungry_state != orig_hungry) { + player.t_flags &= ~ISRUN; + running = FALSE; + to_death = FALSE; + count = 0; + } +} + +/* + * come_down: + * Take the hero down off her acid trip. + */ +void +come_down(struct rogue_state *rs,int arg) +{ + register THING *tp; + register bool seemonst; + + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"come_down\n"); + if (!on(player, ISHALU)) + return; + + kill_daemon(visuals); + player.t_flags &= ~ISHALU; + + if (on(player, ISBLIND)) + return; + + /* + * undo the things + */ + for (tp = lvl_obj; tp != NULL; tp = next(tp)) + if (cansee(rs,tp->o_pos.y, tp->o_pos.x)) + mvaddch(tp->o_pos.y, tp->o_pos.x, tp->o_type); + + /* + * undo the monsters + */ + seemonst = on(player, SEEMONST); + for (tp = mlist; tp != NULL; tp = next(tp)) + { + move(tp->t_pos.y, tp->t_pos.x); + if (cansee(rs,tp->t_pos.y, tp->t_pos.x)) + if (!on(*tp, ISINVIS) || on(player, CANSEE)) + addch(tp->t_disguise); + else + addch(chat(tp->t_pos.y, tp->t_pos.x)); + else if (seemonst) + { + standout(); + addch(tp->t_type); + standend(); + } + } + msg(rs,"Everything looks SO boring now."); +} + +/* + * visuals: + * change the characters for the player + */ +void +visuals(struct rogue_state *rs,int arg) +{ + register THING *tp; + register bool seemonst; + + if (!after || (running && jump)) + return; + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"visuals\n"); + /* + * change the things + */ + for (tp = lvl_obj; tp != NULL; tp = next(tp)) + if (cansee(rs,tp->o_pos.y, tp->o_pos.x)) + mvaddch(tp->o_pos.y, tp->o_pos.x, rnd_thing()); + + /* + * change the stairs + */ + if (!seenstairs && cansee(rs,stairs.y, stairs.x)) + mvaddch(stairs.y, stairs.x, rnd_thing()); + + /* + * change the monsters + */ + seemonst = on(player, SEEMONST); + for (tp = mlist; tp != NULL; tp = next(tp)) + { + move(tp->t_pos.y, tp->t_pos.x); + if (see_monst(tp)) + { + if (tp->t_type == 'X' && tp->t_disguise != 'X') + addch(rnd_thing()); + else + addch(rnd(26) + 'A'); + } + else if (seemonst) + { + standout(); + addch(rnd(26) + 'A'); + standend(); + } + } +} + +/* + * land: + * Land from a levitation potion + */ +void +land(struct rogue_state *rs,int arg) +{ + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"land\n"); + player.t_flags &= ~ISLEVIT; + msg(rs,choose_str("bummer! You've hit the ground", + "you float gently to the ground")); +} + +/* + * turn_see: + * Put on or off seeing monsters on this level + */ +bool +turn_see(struct rogue_state *rs,bool turn_off) +{ + THING *mp; + bool can_see, add_new; + if ( rs->logfp != 0 ) + fprintf(rs->logfp,"turn_see\n"); + + add_new = FALSE; + for (mp = mlist; mp != NULL; mp = next(mp)) + { + move(mp->t_pos.y, mp->t_pos.x); + can_see = see_monst(mp); + if (turn_off) + { + if (!can_see) + addch(mp->t_oldch); + } + else + { + if (!can_see) + standout(); + if (!on(player, ISHALU)) + addch(mp->t_type); + else + addch(rnd(26) + 'A'); + if (!can_see) + { + standend(); + add_new ^= 1;//add_new++; + } + } + } + if (turn_off) + player.t_flags &= ~SEEMONST; + else + player.t_flags |= SEEMONST; + return add_new; +} + diff --git a/src/cc/rogue/extern.c b/src/cc/rogue/extern.c new file mode 100644 index 000000000..fdb77307c --- /dev/null +++ b/src/cc/rogue/extern.c @@ -0,0 +1,516 @@ +/* + * global variable initializaton + * + * @(#)extern.c 4.82 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +#include "rogue.h" + + + +bool after; /* True if we want after daemons */ +bool again; /* Repeating the last command */ +int noscore; /* Was a wizard sometime */ +bool seenstairs; /* Have seen the stairs (for lsd) */ +bool amulet = FALSE; /* He found the amulet */ +bool door_stop = FALSE; /* Stop running when we pass a door */ +bool fight_flush = FALSE; /* True if toilet input */ +bool firstmove = FALSE; /* First move after setting door_stop */ +bool got_ltc = FALSE; /* We have gotten the local tty chars */ +bool has_hit = FALSE; /* Has a "hit" message pending in msg */ +bool in_shell = FALSE; /* True if executing a shell */ +bool inv_describe = TRUE; /* Say which way items are being used */ +bool jump = FALSE; /* Show running as series of jumps */ +bool kamikaze = FALSE; /* to_death really to DEATH */ +bool lower_msg = FALSE; /* Messages should start w/lower case */ +bool move_on = FALSE; /* Next move shouldn't pick up items */ +bool msg_esc = FALSE; /* Check for ESC from msg's --More-- */ +bool passgo = FALSE; /* Follow passages */ +bool playing = TRUE; /* True until he quits */ +bool q_comm = FALSE; /* Are we executing a 'Q' command? */ +bool running = FALSE; /* True if player is running */ +bool save_msg = TRUE; /* Remember last msg */ +bool see_floor = TRUE; /* Show the lamp illuminated floor */ +bool stat_msg = FALSE; /* Should status() print as a msg() */ +bool terse = FALSE; /* True if we should be short */ +bool to_death = FALSE; /* Fighting is to the death! */ +bool tombstone = TRUE; /* Print out tombstone at end */ +#ifdef MASTER +int wizard = FALSE; /* True if allows wizard commands */ +#endif +bool pack_used[26] = { /* Is the character used in the pack? */ + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, + FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE +}; + +char dir_ch; /* Direction from last get_dir() call */ +char runch; /* Direction player is running */ +char take; /* Thing she is taking */ + +int orig_dsusp; /* Original dsusp char */ +char file_name[MAXSTR]; /* Save file name */ +char huh[MAXSTR]; /* The last message printed */ +char prbuf[2*MAXSTR]; /* buffer for sprintfs */ +char whoami[MAXSTR]; /* Name of player */ +char fruit[MAXSTR] = /* Favorite fruit */ + { 's', 'l', 'i', 'm', 'e', '-', 'm', 'o', 'l', 'd', '\0' }; +char home[MAXSTR] = { '\0' }; /* User's home directory */ +char l_last_comm = '\0'; /* Last last_comm */ +char l_last_dir = '\0'; /* Last last_dir */ +char last_comm = '\0'; /* Last command typed */ +char last_dir = '\0'; /* Last direction given */ + +int n_objs; /* # items listed in inventory() call */ +int ntraps; /* Number of traps on this level */ +int hungry_state = 0; /* How hungry is he */ +int inpack = 0; /* Number of things in pack */ +int inv_type = 0; /* Type of inventory to use */ +int level = 1; /* What level she is on */ +int max_hit; /* Max damage done to her in to_death */ +int max_level; /* Deepest player has gone */ +int mpos = 0; /* Where cursor is on top line */ +int no_food = 0; /* Number of levels without food */ + +int count = 0; /* Number of times to repeat command */ +int food_left; /* Amount of food in hero's stomach */ +int lastscore = -1; /* Score before this turn */ +int no_command = 0; /* Number of turns asleep */ +int no_move = 0; /* Number of turns held in place */ +int purse = 0; /* How much gold he has */ +int quiet = 0; /* Number of quiet turns */ +int vf_hit = 0; /* Number of time flytrap has hit */ + + +//int dnum; /* Dungeon number */ +uint64_t seed; /* Random number seed */ + +coord delta; /* Change indicated to get_dir() */ +coord oldpos; /* Position before last look() call */ +coord stairs; /* Location of staircase */ + +PLACE places[MAXLINES*MAXCOLS]; /* level map */ + +const char *p_colors[MAXPOTIONS]; /* Colors of the potions */ +const char *r_stones[MAXRINGS]; /* Stone settings of the rings */ +const char *ws_made[MAXSTICKS]; /* What sticks are made of */ +const char *ws_type[MAXSTICKS]; /* Is it a wand or a staff */ + +THING *cur_armor; /* What he is wearing */ +THING *cur_ring[2]; /* Which rings are being worn */ +THING *cur_weapon; /* Which weapon he is weilding */ +THING *l_last_pick = NULL; /* Last last_pick */ +THING *last_pick = NULL; /* Last object picked in get_item() */ +THING *lvl_obj = NULL; /* List of objects on this level */ +THING *mlist = NULL; /* List of monsters on the level */ +struct room *oldrp; /* Roomin(&oldpos) */ +THING player; /* His stats */ +WINDOW *hw = NULL; /* used as a scratch window */ +char *s_names[MAXSCROLLS]; /* Names of the scrolls */ +FILE *scoreboard = NULL; /* File descriptor for score file */ + +#define INIT_STATS { 16, 0, 1, 10, 12, "1x4", 12 } + +struct stats max_stats = INIT_STATS; /* The maximum for the player */ +struct stats orig_max_stats = INIT_STATS; + +struct monster monsters[26]; +struct room passages[MAXPASS],rooms[MAXROOMS]; /* One for each room -- A level */ +struct obj_info things[NUMTHINGS],ring_info[MAXRINGS],pot_info[MAXPOTIONS],arm_info[MAXARMORS],scr_info[MAXSCROLLS],weap_info[MAXWEAPONS + 1],ws_info[MAXSTICKS]; + +////////////// constants +#define ___ 1 +#define XX 10 + +const struct obj_info origthings[NUMTHINGS] = { + { 0, 26 }, /* potion */ + { 0, 36 }, /* scroll */ + { 0, 16 }, /* food */ + { 0, 7 }, /* weapon */ + { 0, 7 }, /* armor */ + { 0, 4 }, /* ring */ + { 0, 4 }, /* stick */ +}; + +const struct room origpassages[MAXPASS] = /* One for each passage */ +{ + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} }, + { {0, 0}, {0, 0}, {0, 0}, 0, ISGONE|ISDARK, 0, {{0,0}} } +}; +const struct monster origmonsters[26] = + { +/* Name CARRY FLAG str, exp, lvl, amr, hpt, dmg */ +{ "aquator", 0, ISMEAN, { XX, 20, 5, 2, ___, "0x0/0x0" } }, +{ "bat", 0, ISFLY, { XX, 1, 1, 3, ___, "1x2" } }, +{ "centaur", 15, 0, { XX, 17, 4, 4, ___, "1x2/1x5/1x5" } }, +{ "dragon", 100, ISMEAN, { XX,5000, 10, -1, ___, "1x8/1x8/3x10" } }, +{ "emu", 0, ISMEAN, { XX, 2, 1, 7, ___, "1x2" } }, +{ "venus flytrap", 0, ISMEAN, { XX, 80, 8, 3, ___, "%%%x0" } }, + /* NOTE: the damage is %%% so that xstr won't merge this */ + /* string with others, since it is written on in the program */ +{ "griffin", 20, ISMEAN|ISFLY|ISREGEN, { XX,2000, 13, 2, ___, "4x3/3x5" } }, +{ "hobgoblin", 0, ISMEAN, { XX, 3, 1, 5, ___, "1x8" } }, +{ "ice monster", 0, 0, { XX, 5, 1, 9, ___, "0x0" } }, +{ "jabberwock", 70, 0, { XX,3000, 15, 6, ___, "2x12/2x4" } }, +{ "kestrel", 0, ISMEAN|ISFLY, { XX, 1, 1, 7, ___, "1x4" } }, +{ "leprechaun", 0, 0, { XX, 10, 3, 8, ___, "1x1" } }, +{ "medusa", 40, ISMEAN, { XX,200, 8, 2, ___, "3x4/3x4/2x5" } }, +{ "nymph", 100, 0, { XX, 37, 3, 9, ___, "0x0" } }, +{ "orc", 15, ISGREED,{ XX, 5, 1, 6, ___, "1x8" } }, +{ "phantom", 0, ISINVIS,{ XX,120, 8, 3, ___, "4x4" } }, +{ "quagga", 0, ISMEAN, { XX, 15, 3, 3, ___, "1x5/1x5" } }, +{ "rattlesnake", 0, ISMEAN, { XX, 9, 2, 3, ___, "1x6" } }, +{ "snake", 0, ISMEAN, { XX, 2, 1, 5, ___, "1x3" } }, +{ "troll", 50, ISREGEN|ISMEAN,{ XX, 120, 6, 4, ___, "1x8/1x8/2x6" } }, +{ "black unicorn", 0, ISMEAN, { XX,190, 7, -2, ___, "1x9/1x9/2x9" } }, +{ "vampire", 20, ISREGEN|ISMEAN,{ XX,350, 8, 1, ___, "1x10" } }, +{ "wraith", 0, 0, { XX, 55, 5, 4, ___, "1x6" } }, +{ "xeroc", 30, 0, { XX,100, 7, 7, ___, "4x4" } }, +{ "yeti", 30, 0, { XX, 50, 4, 6, ___, "1x6/1x6" } }, +{ "zombie", 0, ISMEAN, { XX, 6, 2, 8, ___, "1x8" } } + }; +#undef ___ +#undef XX + +const struct obj_info origarm_info[MAXARMORS] = { + { "leather armor", 20, 20, NULL, FALSE }, + { "ring mail", 15, 25, NULL, FALSE }, + { "studded leather armor", 15, 20, NULL, FALSE }, + { "scale mail", 13, 30, NULL, FALSE }, + { "chain mail", 12, 75, NULL, FALSE }, + { "splint mail", 10, 80, NULL, FALSE }, + { "banded mail", 10, 90, NULL, FALSE }, + { "plate mail", 5, 150, NULL, FALSE }, +}; +const struct obj_info origpot_info[MAXPOTIONS] = { + { "confusion", 7, 5, NULL, FALSE }, + { "hallucination", 8, 5, NULL, FALSE }, + { "poison", 8, 5, NULL, FALSE }, + { "gain strength", 13, 150, NULL, FALSE }, + { "see invisible", 3, 100, NULL, FALSE }, + { "healing", 13, 130, NULL, FALSE }, + { "monster detection", 6, 130, NULL, FALSE }, + { "magic detection", 6, 105, NULL, FALSE }, + { "raise level", 2, 250, NULL, FALSE }, + { "extra healing", 5, 200, NULL, FALSE }, + { "haste self", 5, 190, NULL, FALSE }, + { "restore strength", 13, 130, NULL, FALSE }, + { "blindness", 5, 5, NULL, FALSE }, + { "levitation", 6, 75, NULL, FALSE }, +}; +const struct obj_info origring_info[MAXRINGS] = { + { "protection", 9, 400, NULL, FALSE }, + { "add strength", 9, 400, NULL, FALSE }, + { "sustain strength", 5, 280, NULL, FALSE }, + { "searching", 10, 420, NULL, FALSE }, + { "see invisible", 10, 310, NULL, FALSE }, + { "adornment", 1, 10, NULL, FALSE }, + { "aggravate monster", 10, 10, NULL, FALSE }, + { "dexterity", 8, 440, NULL, FALSE }, + { "increase damage", 8, 400, NULL, FALSE }, + { "regeneration", 4, 460, NULL, FALSE }, + { "slow digestion", 9, 240, NULL, FALSE }, + { "teleportation", 5, 30, NULL, FALSE }, + { "stealth", 7, 470, NULL, FALSE }, + { "maintain armor", 5, 380, NULL, FALSE }, +}; +const struct obj_info origscr_info[MAXSCROLLS] = { + { "monster confusion", 7, 140, NULL, FALSE }, + { "magic mapping", 4, 150, NULL, FALSE }, + { "hold monster", 2, 180, NULL, FALSE }, + { "sleep", 3, 5, NULL, FALSE }, + { "enchant armor", 7, 160, NULL, FALSE }, + { "identify potion", 10, 80, NULL, FALSE }, + { "identify scroll", 10, 80, NULL, FALSE }, + { "identify weapon", 6, 80, NULL, FALSE }, + { "identify armor", 7, 100, NULL, FALSE }, + { "identify ring, wand or staff", 10, 115, NULL, FALSE }, + { "scare monster", 3, 200, NULL, FALSE }, + { "food detection", 2, 60, NULL, FALSE }, + { "teleportation", 5, 165, NULL, FALSE }, + { "enchant weapon", 8, 150, NULL, FALSE }, + { "create monster", 4, 75, NULL, FALSE }, + { "remove curse", 7, 105, NULL, FALSE }, + { "aggravate monsters", 3, 20, NULL, FALSE }, + { "protect armor", 2, 250, NULL, FALSE }, +}; +const struct obj_info origweap_info[MAXWEAPONS + 1] = { + { "mace", 11, 8, NULL, FALSE }, + { "long sword", 11, 15, NULL, FALSE }, + { "short bow", 12, 15, NULL, FALSE }, + { "arrow", 12, 1, NULL, FALSE }, + { "dagger", 8, 3, NULL, FALSE }, + { "two handed sword", 10, 75, NULL, FALSE }, + { "dart", 12, 2, NULL, FALSE }, + { "shuriken", 12, 5, NULL, FALSE }, + { "spear", 12, 5, NULL, FALSE }, + { NULL, 0 }, /* DO NOT REMOVE: fake entry for dragon's breath */ +}; +const struct obj_info origws_info[MAXSTICKS] = { + { "light", 12, 250, NULL, FALSE }, + { "invisibility", 6, 5, NULL, FALSE }, + { "lightning", 3, 330, NULL, FALSE }, + { "fire", 3, 330, NULL, FALSE }, + { "cold", 3, 330, NULL, FALSE }, + { "polymorph", 15, 310, NULL, FALSE }, + { "magic missile", 10, 170, NULL, FALSE }, + { "haste monster", 10, 5, NULL, FALSE }, + { "slow monster", 11, 350, NULL, FALSE }, + { "drain life", 9, 300, NULL, FALSE }, + { "nothing", 1, 5, NULL, FALSE }, + { "teleport away", 6, 340, NULL, FALSE }, + { "teleport to", 6, 50, NULL, FALSE }, + { "cancellation", 5, 280, NULL, FALSE }, +}; + +const struct h_list helpstr[] = { + {'?', " prints help", TRUE}, + {'/', " identify object", TRUE}, + {'h', " left", TRUE}, + {'j', " down", TRUE}, + {'k', " up", TRUE}, + {'l', " right", TRUE}, + {'y', " up & left", TRUE}, + {'u', " up & right", TRUE}, + {'b', " down & left", TRUE}, + {'n', " down & right", TRUE}, + {'H', " run left", FALSE}, + {'J', " run down", FALSE}, + {'K', " run up", FALSE}, + {'L', " run right", FALSE}, + {'Y', " run up & left", FALSE}, + {'U', " run up & right", FALSE}, + {'B', " run down & left", FALSE}, + {'N', " run down & right", FALSE}, + {CTRL('H'), " run left until adjacent", FALSE}, + {CTRL('J'), " run down until adjacent", FALSE}, + {CTRL('K'), " run up until adjacent", FALSE}, + {CTRL('L'), " run right until adjacent", FALSE}, + {CTRL('Y'), " run up & left until adjacent", FALSE}, + {CTRL('U'), " run up & right until adjacent", FALSE}, + {CTRL('B'), " run down & left until adjacent", FALSE}, + {CTRL('N'), " run down & right until adjacent", FALSE}, + {'\0', " : run that way", TRUE}, + {'\0', " : run till adjacent", TRUE}, + {'f', " fight till death or near death", TRUE}, + {'t', " throw something", TRUE}, + {'m', " move onto without picking up", TRUE}, + {'z', " zap a wand in a direction", TRUE}, + {'^', " identify trap type", TRUE}, + {'s', " search for trap/secret door", TRUE}, + {'>', " go down a staircase", TRUE}, + {'<', " go up a staircase", TRUE}, + {'.', " rest for a turn", TRUE}, + {',', " pick something up", TRUE}, + {'i', " inventory", TRUE}, + {'I', " inventory single item", TRUE}, + {'q', " quaff potion", TRUE}, + {'r', " read scroll", TRUE}, + {'e', " eat food", TRUE}, + {'w', " wield a weapon", TRUE}, + {'W', " wear armor", TRUE}, + {'T', " take armor off", TRUE}, + {'P', " put on ring", TRUE}, + {'R', " remove ring", TRUE}, + {'d', " drop object", TRUE}, + {'c', " call object", TRUE}, + {'a', " repeat last command", TRUE}, + {')', " print current weapon", TRUE}, + {']', " print current armor", TRUE}, + {'=', " print current rings", TRUE}, + {'@', " print current stats", TRUE}, + {'D', " recall what's been discovered", TRUE}, + {'o', " examine/set options", TRUE}, + {CTRL('R'), " redraw screen", TRUE}, + {CTRL('P'), " repeat last message", TRUE}, + {ESCAPE, " cancel command", TRUE}, + {'S', " save game", TRUE}, + {'Q', " quit", TRUE}, + {'!', " shell escape", TRUE}, + {'F', " fight till either of you dies", TRUE}, + {'v', " print version number", TRUE}, + {0, NULL } +}; + +const char *inv_t_name[] = { + "Overwrite", + "Slow", + "Clear" +}; + +const char *tr_name[] = { /* Names of the traps */ + "a trapdoor", + "an arrow trap", + "a sleeping gas trap", + "a beartrap", + "a teleport trap", + "a poison dart trap", + "a rust trap", + "a mysterious trap" +}; + +const int32_t a_class[MAXARMORS] = { /* Armor class for each armor type */ + 8, /* LEATHER */ + 7, /* RING_MAIL */ + 7, /* STUDDED_LEATHER */ + 6, /* SCALE_MAIL */ + 5, /* CHAIN_MAIL */ + 4, /* SPLINT_MAIL */ + 4, /* BANDED_MAIL */ + 3, /* PLATE_MAIL */ +}; + +const int32_t e_levels[] = { + 10L, + 20L, + 40L, + 80L, + 160L, + 320L, + 640L, + 1300L, + 2600L, + 5200L, + 13000L, + 26000L, + 50000L, + 100000L, + 200000L, + 400000L, + 800000L, + 2000000L, + 4000000L, + 8000000L, + 0L +}; + +#include +extern int between; +extern int group; +extern coord nh; + +void externs_clear() +{ + int i; + after = 0; /* True if we want after daemons */ + again = 0; /* Repeating the last command */ + noscore = 0; /* Was a wizard sometime */ + seenstairs = 0; /* Have seen the stairs (for lsd) */ + amulet = FALSE; /* He found the amulet */ + door_stop = FALSE; /* Stop running when we pass a door */ + fight_flush = FALSE; /* True if toilet input */ + firstmove = FALSE; /* First move after setting door_stop */ + got_ltc = FALSE; /* We have gotten the local tty chars */ + has_hit = FALSE; /* Has a "hit" message pending in msg */ + in_shell = FALSE; /* True if executing a shell */ + inv_describe = TRUE; /* Say which way items are being used */ + jump = FALSE; /* Show running as series of jumps */ + kamikaze = FALSE; /* to_death really to DEATH */ + lower_msg = FALSE; /* Messages should start w/lower case */ + move_on = FALSE; /* Next move shouldn't pick up items */ + msg_esc = FALSE; /* Check for ESC from msg's --More-- */ + passgo = FALSE; /* Follow passages */ + playing = TRUE; /* True until he quits */ + q_comm = FALSE; /* Are we executing a 'Q' command? */ + running = FALSE; /* True if player is running */ + save_msg = TRUE; /* Remember last msg */ + see_floor = TRUE; /* Show the lamp illuminated floor */ + stat_msg = FALSE; /* Should status() print as a msg() */ + terse = FALSE; /* True if we should be short */ + to_death = FALSE; /* Fighting is to the death! */ + tombstone = TRUE; /* Print out tombstone at end */ +#ifdef MASTER + int wizard = FALSE; /* True if allows wizard commands */ +#endif + for (i=0; i<26; i++) + pack_used[i] = FALSE; + for (i=0; i +#include + +#ifdef HAVE_CONFIG_H +#ifdef PDCURSES +#undef HAVE_UNISTD_H +#undef HAVE_LIMITS_H +#undef HAVE_MEMORY_H +#undef HAVE_STRING_H +#endif +#include +#include "config.h" +#elif defined(__DJGPP__) +#define HAVE_SYS_TYPES_H 1 +#define HAVE_PROCESS_H 1 +#define HAVE_PWD_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_SETGID 1 +#define HAVE_GETGID 1 +#define HAVE_SETUID 1 +#define HAVE_GETUID 1 +#define HAVE_GETPASS 1 +#define HAVE_SPAWNL 1 +#define HAVE_ALARM 1 +#define HAVE_ERASECHAR 1 +#define HAVE_KILLCHAR 1 +#elif defined(_WIN32) +#define HAVE_CURSES_H +#define HAVE_TERM_H +#define HAVE__SPAWNL +#define HAVE_SYS_TYPES_H +#define HAVE_PROCESS_H +#define HAVE_ERASECHAR 1 +#define HAVE_KILLCHAR 1 +#elif defined(__CYGWIN__) +#define HAVE_SYS_TYPES_H 1 +#define HAVE_PWD_H 1 +#define HAVE_PWD_H 1 +#define HAVE_SYS_UTSNAME_H 1 +#define HAVE_ARPA_INET_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_NCURSES_TERM_H 1 +#define HAVE_ESCDELAY +#define HAVE_SETGID 1 +#define HAVE_GETGID 1 +#define HAVE_SETUID 1 +#define HAVE_GETUID 1 +#define HAVE_GETPASS 1 +#define HAVE_GETPWUID 1 +#define HAVE_WORKING_FORK 1 +#define HAVE_ALARM 1 +#define HAVE_SPAWNL 1 +#define HAVE__SPAWNL 1 +#define HAVE_ERASECHAR 1 +#define HAVE_KILLCHAR 1 +#else /* POSIX */ +#define HAVE_SYS_TYPES_H 1 +#define HAVE_PWD_H 1 +#define HAVE_PWD_H 1 +#define HAVE_SYS_UTSNAME_H 1 +#define HAVE_ARPA_INET_H 1 +#define HAVE_UNISTD_H 1 +#define HAVE_TERMIOS_H 1 +#define HAVE_TERM_H 1 +#define HAVE_SETGID 1 +#define HAVE_GETGID 1 +#define HAVE_SETUID 1 +#define HAVE_GETUID 1 +#define HAVE_SETREUID 1 +#define HAVE_SETREGID 1 +#define HAVE_GETPASS 1 +#define HAVE_GETPWUID 1 +#define HAVE_WORKING_FORK 1 +#define HAVE_ERASECHAR 1 +#define HAVE_KILLCHAR 1 +#ifndef _AIX +#define HAVE_GETLOADAVG 1 +#endif +#define HAVE_ALARM 1 +#endif + +#ifdef __DJGPP__ +#undef HAVE_GETPWUID /* DJGPP's limited version doesn't even work as documented */ +#endif + +/* + * Don't change the constants, since they are used for sizes in many + * places in the program. + */ + +#include +#include + +#ifdef _WIN32 +#ifdef _MSC_VER +#include +#endif +#endif + +#undef SIGTSTP + +#define MAXSTR 1024 /* maximum length of strings */ +#define MAXLINES 32 /* maximum number of screen lines used */ +#define MAXCOLS 80 /* maximum number of screen columns used */ + +#define RN ((int32_t)((seed = seed*11109+13849) >> 16) & 0xffff) +#ifdef CTRL +#undef CTRL +#endif +#define CTRL(c) (c & 037) + +/* + * Now all the global variables + */ + +extern bool got_ltc, in_shell; +extern int wizard; +extern char fruit[], prbuf[], whoami[]; +extern int orig_dsusp; +extern FILE *scoreboard; + +/* + * Function types + */ +void externs_clear(); + +void auto_save(int); +void endit(int sig); +void fatal(char *); +void getltchars(void); +void leave(int); +void my_exit(int st); +void playltchars(void); +void quit(int); +int32_t _quit(); + +void resetltchars(void); +void set_order(int *order, int numthings); +void tstp(int ignored); + +char *killname(char monst, bool doart); +char *nothing(char type); +char *type_name(int type); + +#ifdef CHECKTIME +int checkout(void); +#endif + +int md_chmod(char *filename, int mode); +char *md_crypt(char *key, char *salt); +int md_dsuspchar(void); +int md_erasechar(void); +char *md_gethomedir(void); +char *md_getusername(void); +int md_getuid(void); +char *md_getpass(char *prompt); +int md_getpid(void); +char *md_getrealname(int uid); +void md_init(void); +int md_killchar(void); +void md_normaluser(void); +void md_raw_standout(void); +void md_raw_standend(void); +int md_readchar(void); +int md_setdsuspchar(int c); +int md_shellescape(void); +void md_sleep(int s); +int md_suspchar(void); +int md_hasclreol(void); +int md_unlink(char *file); +int md_unlink_open_file(char *file, FILE *inf); +void md_tstpsignal(void); +void md_tstphold(void); +void md_tstpresume(void); +void md_ignoreallsignals(void); +void md_onsignal_autosave(void); +void md_onsignal_exit(void); +void md_onsignal_default(void); +int md_issymlink(char *sp); + +int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); + +#endif + diff --git a/src/cc/rogue/fight.c b/src/cc/rogue/fight.c new file mode 100644 index 000000000..9cb4bf61e --- /dev/null +++ b/src/cc/rogue/fight.c @@ -0,0 +1,681 @@ +/* + * All the fighting gets done here + * + * @(#)fight.c 4.67 (Berkeley) 09/06/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +//#include +#include "rogue.h" + +//#define EQSTR(a, b) (strcmp(a, b) == 0) + +const char *h_names[] = { /* strings for hitting */ + " scored an excellent hit on ", + " hit ", + " have injured ", + " swing and hit ", + " scored an excellent hit on ", + " hit ", + " has injured ", + " swings and hits " +}; + +const char *m_names[] = { /* strings for missing */ + " miss", + " swing and miss", + " barely miss", + " don't hit", + " misses", + " swings and misses", + " barely misses", + " doesn't hit", +}; + +/* + * adjustments to hit probabilities due to strength + */ +static const int str_plus[] = { + -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, + 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, +}; + +/* + * adjustments to damage done due to strength + */ +static const int add_dam[] = { + -7, -6, -5, -4, -3, -2, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 2, 3, + 3, 4, 5, 5, 5, 5, 5, 5, 5, 5, 5, 6 +}; + +/* + * fight: + * The player attacks the monster. + */ +int +fight(struct rogue_state *rs,coord *mp, THING *weap, bool thrown) +{ + register THING *tp; + register bool did_hit = TRUE; + register char *mname, ch; + + /* + * Find the monster we want to fight + */ +#ifdef MASTER + if ((tp = moat(mp->y, mp->x)) == NULL) + debug("Fight what @ %d,%d", mp->y, mp->x); +#else + tp = moat(mp->y, mp->x); +#endif + /* + * Since we are fighting, things are not quiet so no healing takes + * place. + */ + count = 0; + quiet = 0; + runto(rs,mp); + /* + * Let him know it was really a xeroc (if it was one). + */ + ch = '\0'; + if (tp->t_type == 'X' && tp->t_disguise != 'X' && !on(player, ISBLIND)) + { + tp->t_disguise = 'X'; + if (on(player, ISHALU)) { + ch = (char)(rnd(26) + 'A'); + mvaddch(tp->t_pos.y, tp->t_pos.x, ch); + } + msg(rs,choose_str("heavy! That's a nasty critter!", + "wait! That's a xeroc!")); + if (!thrown) + return FALSE; + } + mname = set_mname(tp); + did_hit = FALSE; + has_hit = (terse && !to_death); + if (roll_em(&player, tp, weap, thrown)) + { + did_hit = FALSE; + if (thrown) + thunk(rs,weap, mname, terse); + else + hit(rs,(char *) NULL, mname, terse); + if (on(player, CANHUH)) + { + did_hit = TRUE; + tp->t_flags |= ISHUH; + player.t_flags &= ~CANHUH; + endmsg(rs); + has_hit = FALSE; + msg(rs,"your hands stop glowing %s", pick_color("red")); + } + if (tp->t_stats.s_hpt <= 0) + killed(rs,tp, TRUE); + else if (did_hit && !on(player, ISBLIND)) + msg(rs,"%s appears confused", mname); + did_hit = TRUE; + } + else + if (thrown) + bounce(rs,weap, mname, terse); + else + miss(rs,(char *) NULL, mname, terse); + return did_hit; +} + +/* + * attack: + * The monster attacks the player + */ +int +attack(struct rogue_state *rs,THING *mp) +{ + register char *mname; + register int oldhp; + + /* + * Since this is an attack, stop running and any healing that was + * going on at the time. + */ + running = FALSE; + count = 0; + quiet = 0; + if (to_death && !on(*mp, ISTARGET)) + { + to_death = FALSE; + kamikaze = FALSE; + } + if (mp->t_type == 'X' && mp->t_disguise != 'X' && !on(player, ISBLIND)) + { + mp->t_disguise = 'X'; + if (on(player, ISHALU)) + mvaddch(mp->t_pos.y, mp->t_pos.x, rnd(26) + 'A'); + } + mname = set_mname(mp); + oldhp = pstats.s_hpt; + if (roll_em(mp, &player, (THING *) NULL, FALSE)) + { + if (mp->t_type != 'I') + { + if (has_hit) + addmsg(rs,". "); + hit(rs,mname, (char *) NULL, FALSE); + } + else + if (has_hit) + endmsg(rs); + has_hit = FALSE; + if (pstats.s_hpt <= 0) + death(rs,mp->t_type); /* Bye bye life ... */ + else if (!kamikaze) + { + oldhp -= pstats.s_hpt; + if (oldhp > max_hit) + max_hit = oldhp; + if (pstats.s_hpt <= max_hit) + to_death = FALSE; + } + if (!on(*mp, ISCANC)) + switch (mp->t_type) + { + case 'A': + /* + * If an aquator hits, you can lose armor class. + */ + rust_armor(rs,cur_armor); + when 'I': + /* + * The ice monster freezes you + */ + player.t_flags &= ~ISRUN; + if (!no_command) + { + addmsg(rs,"you are frozen"); + if (!terse) + addmsg(rs," by the %s", mname); + endmsg(rs); + } + no_command += rnd(2) + 2; + if (no_command > BORE_LEVEL) + death(rs,'h'); + when 'R': + /* + * Rattlesnakes have poisonous bites + */ + if (!save(VS_POISON)) + { + if (!ISWEARING(R_SUSTSTR)) + { + chg_str(-1); + if (!terse) + msg(rs,"you feel a bite in your leg and now feel weaker"); + else + msg(rs,"a bite has weakened you"); + } + else if (!to_death) + { + if (!terse) + msg(rs,"a bite momentarily weakens you"); + else + msg(rs,"bite has no effect"); + } + } + when 'W': + case 'V': + /* + * Wraiths might drain energy levels, and Vampires + * can steal max_hp + */ + if (rnd(100) < (mp->t_type == 'W' ? 15 : 30)) + { + register int fewer; + + if (mp->t_type == 'W') + { + if (pstats.s_exp == 0) + death(rs,'W'); /* All levels gone */ + if (--pstats.s_lvl == 0) + { + pstats.s_exp = 0; + pstats.s_lvl = 1; + } + else + pstats.s_exp = e_levels[pstats.s_lvl-1]+1; + fewer = roll(1, 10); + } + else + fewer = roll(1, 3); + pstats.s_hpt -= fewer; + max_hp -= fewer; + if (pstats.s_hpt <= 0) + pstats.s_hpt = 1; + if (max_hp <= 0) + death(rs,mp->t_type); + msg(rs,"you suddenly feel weaker"); + } + when 'F': + /* + * Venus Flytrap stops the poor guy from moving + */ + player.t_flags |= ISHELD; + sprintf(monsters['F'-'A'].m_stats.s_dmg,"%dx1", ++vf_hit); + if (--pstats.s_hpt <= 0) + death(rs,'F'); + when 'L': + { + /* + * Leperachaun steals some gold + */ + register int lastpurse; + + lastpurse = purse; + purse -= GOLDCALC; + if (!save(VS_MAGIC)) + purse -= GOLDCALC + GOLDCALC + GOLDCALC + GOLDCALC; + if (purse < 0) + purse = 0; + remove_mon(rs,&mp->t_pos, mp, FALSE); + mp=NULL; + if (purse != lastpurse) + msg(rs,"your purse feels lighter"); + } + when 'N': + { + THING *obj, *steal; int nobj; + + /* + * Nymph's steal a magic item, look through the pack + * and pick out one we like. + */ + steal = NULL; + for (nobj = 0, obj = pack; obj != NULL; obj = next(obj)) + if (obj != cur_armor && obj != cur_weapon + && obj != cur_ring[LEFT] && obj != cur_ring[RIGHT] + && is_magic(obj) && rnd(++nobj) == 0) + steal = obj; + if (steal != NULL) + { + remove_mon(rs,&mp->t_pos, moat(mp->t_pos.y, mp->t_pos.x), FALSE); + mp=NULL; + leave_pack(rs,steal, FALSE, FALSE); + msg(rs,"she stole %s!", inv_name(steal, TRUE)); + if ( steal->o_count <= 0 ) + discard(steal); + } + } + otherwise: + break; + } + } + else if (mp->t_type != 'I') + { + if (has_hit) + { + addmsg(rs,". "); + has_hit = FALSE; + } + if (mp->t_type == 'F') + { + pstats.s_hpt -= vf_hit; + if (pstats.s_hpt <= 0) + death(rs,mp->t_type); /* Bye bye life ... */ + } + miss(rs,mname, (char *) NULL, FALSE); + } + if (fight_flush && !to_death) + flush_type(); + count = 0; + status(rs); + if (mp == NULL) + return(-1); + else + return(0); +} + +/* + * set_mname: + * return the monster name for the given monster + */ +char * +set_mname(THING *tp) +{ + int ch; + char *mname; + static char tbuf[MAXSTR] = { 't', 'h', 'e', ' ' }; + + if (!see_monst(tp) && !on(player, SEEMONST)) + return (terse ? (char *)"it" : (char *)"something"); + else if (on(player, ISHALU)) + { + move(tp->t_pos.y, tp->t_pos.x); + ch = toascii(inch()); + if (!isupper(ch)) + ch = rnd(26); + else + ch -= 'A'; + mname = monsters[ch].m_name; + } + else + mname = monsters[tp->t_type - 'A'].m_name; + strcpy(&tbuf[4], mname); + return tbuf; +} + +/* + * swing: + * Returns true if the swing hits + */ +int +swing(int at_lvl, int op_arm, int wplus) +{ + int res = rnd(20); + int need = (20 - at_lvl) - op_arm; + + return (res + wplus >= need); +} + +/* + * roll_em: + * Roll several attacks + */ +bool +roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl) +{ + register struct stats *att, *def; + register char *cp; + register int ndice, nsides, def_arm; + register bool did_hit = FALSE; + register int hplus; + register int dplus; + register int damage; + + att = &thatt->t_stats; + def = &thdef->t_stats; + if (weap == NULL) + { + cp = att->s_dmg; + dplus = 0; + hplus = 0; + } + else + { + hplus = (weap == NULL ? 0 : weap->o_hplus); + dplus = (weap == NULL ? 0 : weap->o_dplus); + if (weap == cur_weapon) + { + if (ISRING(LEFT, R_ADDDAM)) + dplus += cur_ring[LEFT]->o_arm; + else if (ISRING(LEFT, R_ADDHIT)) + hplus += cur_ring[LEFT]->o_arm; + if (ISRING(RIGHT, R_ADDDAM)) + dplus += cur_ring[RIGHT]->o_arm; + else if (ISRING(RIGHT, R_ADDHIT)) + hplus += cur_ring[RIGHT]->o_arm; + } + cp = weap->o_damage; + if (hurl) + { + if ((weap->o_flags&ISMISL) && cur_weapon != NULL && + cur_weapon->o_which == weap->o_launch) + { + cp = weap->o_hurldmg; + hplus += cur_weapon->o_hplus; + dplus += cur_weapon->o_dplus; + } + else if (weap->o_launch < 0) + cp = weap->o_hurldmg; + } + } + /* + * If the creature being attacked is not running (alseep or held) + * then the attacker gets a plus four bonus to hit. + */ + if (!on(*thdef, ISRUN)) + hplus += 4; + def_arm = def->s_arm; + if (def == &pstats) + { + if (cur_armor != NULL) + def_arm = cur_armor->o_arm; + if (ISRING(LEFT, R_PROTECT)) + def_arm -= cur_ring[LEFT]->o_arm; + if (ISRING(RIGHT, R_PROTECT)) + def_arm -= cur_ring[RIGHT]->o_arm; + } + while(cp != NULL && *cp != '\0') + { + ndice = atoi(cp); + if ((cp = strchr(cp, 'x')) == NULL) + break; + nsides = atoi(++cp); + if (swing(att->s_lvl, def_arm, hplus + str_plus[att->s_str])) + { + int proll; + + proll = roll(ndice, nsides); +#ifdef MASTER + if (ndice + nsides > 0 && proll <= 0) + debug("Damage for %dx%d came out %d, dplus = %d, add_dam = %d, def_arm = %d", ndice, nsides, proll, dplus, add_dam[att->s_str], def_arm); +#endif + damage = dplus + proll + add_dam[att->s_str]; + def->s_hpt -= max(0, damage); + did_hit = TRUE; + } + if ((cp = strchr(cp, '/')) == NULL) + break; + cp++; + } + return did_hit; +} + +/* + * prname: + * The print name of a combatant + */ +char * +prname(char *mname, bool upper) +{ + static char tbuf[MAXSTR]; + + *tbuf = '\0'; + if (mname == 0) + strcpy(tbuf, "you"); + else + strcpy(tbuf, mname); + if (upper) + *tbuf = (char) toupper(*tbuf); + return tbuf; +} + +/* + * thunk: + * A missile hits a monster + */ +void +thunk(struct rogue_state *rs,THING *weap, char *mname, bool noend) +{ + if (to_death) + return; + if (weap->o_type == WEAPON) + addmsg(rs,"the %s hits ", weap_info[weap->o_which].oi_name); + else + addmsg(rs,"you hit "); + addmsg(rs,"%s", mname); + if (!noend) + endmsg(rs); +} + +/* + * hit: + * Print a message to indicate a succesful hit + */ + +void +hit(struct rogue_state *rs,char *er, char *ee, bool noend) +{ + int32_t i; const char *s; + + if (to_death) + return; + addmsg(rs,prname(er, TRUE)); + if (terse) + s = " hit"; + else + { + i = rnd(4); + if (er != NULL) + i += 4; + s = h_names[i]; + } + addmsg(rs,(char *)s); + if (!terse) + addmsg(rs,prname(ee, FALSE)); + if (!noend) + endmsg(rs); +} + +/* + * miss: + * Print a message to indicate a poor swing + */ +void +miss(struct rogue_state *rs,char *er, char *ee, bool noend) +{ + int i; + + if (to_death) + return; + addmsg(rs,prname(er, TRUE)); + if (terse) + i = 0; + else + i = rnd(4); + if (er != NULL) + i += 4; + addmsg(rs,(char *)m_names[i]); + if (!terse) + addmsg(rs," %s", prname(ee, FALSE)); + if (!noend) + endmsg(rs); +} + +/* + * bounce: + * A missile misses a monster + */ +void +bounce(struct rogue_state *rs,THING *weap, char *mname, bool noend) +{ + if (to_death) + return; + if (weap->o_type == WEAPON) + addmsg(rs,"the %s misses ", weap_info[weap->o_which].oi_name); + else + addmsg(rs,"you missed "); + addmsg(rs,mname); + if (!noend) + endmsg(rs); +} + +/* + * remove_mon: + * Remove a monster from the screen + */ +void +remove_mon(struct rogue_state *rs,coord *mp, THING *tp, bool waskill) +{ + register THING *obj, *nexti; + for (obj = tp->t_pack; obj != NULL; obj = nexti) + { + nexti = next(obj); + obj->o_pos = tp->t_pos; + detach(tp->t_pack, obj); + if (waskill) + fall(rs,obj, FALSE); + else discard(obj); + } + moat(mp->y, mp->x) = NULL; + mvaddch(mp->y, mp->x, tp->t_oldch); + detach(mlist, tp); + if (on(*tp, ISTARGET)) + { + kamikaze = FALSE; + to_death = FALSE; + if (fight_flush) + flush_type(); + } + discard(tp); +} + +/* + * killed: + * Called to put a monster to death + */ +void +killed(struct rogue_state *rs,THING *tp, bool pr) +{ + char *mname; + + pstats.s_exp += tp->t_stats.s_exp; + + /* + * If the monster was a venus flytrap, un-hold him + */ + switch (tp->t_type) + { + case 'F': + player.t_flags &= ~ISHELD; + vf_hit = 0; + strcpy(monsters['F'-'A'].m_stats.s_dmg, "000x0"); + when 'L': + { + THING *gold; + + if (fallpos(&tp->t_pos, &tp->t_room->r_gold) && level >= max_level) + { + gold = new_item(); + gold->o_type = GOLD; + gold->o_goldval = GOLDCALC; + if (save(VS_MAGIC)) + gold->o_goldval += GOLDCALC + GOLDCALC + + GOLDCALC + GOLDCALC; + attach(tp->t_pack, gold); + } + } + } + /* + * Get rid of the monster. + */ + mname = set_mname(tp); + remove_mon(rs,&tp->t_pos, tp, TRUE); + if (pr) + { + if (has_hit) + { + addmsg(rs,". Defeated "); + has_hit = FALSE; + } + else + { + if (!terse) + addmsg(rs,"you have "); + addmsg(rs,"defeated "); + } + msg(rs,mname); + } + /* + * Do adjustments if he went up a level + */ + check_level(rs); + if (fight_flush) + flush_type(); +} diff --git a/src/cc/rogue/init.c b/src/cc/rogue/init.c new file mode 100644 index 000000000..b11cfa80c --- /dev/null +++ b/src/cc/rogue/init.c @@ -0,0 +1,495 @@ +/* + * global variable initializaton + * + * @(#)init.c 4.31 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +//#include +#include "rogue.h" + +/* + * init_player: + * Roll her up + */ +void rogue_restoreobject(THING *o,struct rogue_packitem *item); + +int32_t rogue_total(THING *o) +{ + if ( (o->o_flags & ISMANY) != 0 ) + return(1); + else return(o->o_count); +} + +void restore_player(struct rogue_state *rs) +{ + int32_t i,total = 0; THING *obj; + //rs->P.gold = purse; + max_hp = rs->P.hitpoints; + //pstats.s_hpt = max_hp; + pstats.s_str = rs->P.strength & 0xffff; + if ( (max_stats.s_str= (rs->P.strength >> 16) & 0xffff) == 0 ) + max_stats.s_str = 16; + if ( pstats.s_str > max_stats.s_str ) + pstats.s_str = max_stats.s_str; + pstats.s_lvl = rs->P.level; + pstats.s_exp = rs->P.experience; + for (i=0; iP.packsize&&iP.roguepack[i]); + total += rogue_total(obj); + if ( total > ROGUE_MAXTOTAL ) + break; + add_pack(rs,obj,TRUE); + } +} + +void init_player(struct rogue_state *rs) +{ + register THING *obj; int32_t i; + pstats = max_stats; + food_left = HUNGERTIME; + + if ( rs->restoring != 0 ) + { + // duplicate rng usage of normal case + obj = new_item(); + init_weapon(obj, MACE); + free(obj); + obj = new_item(); + init_weapon(obj, BOW); + free(obj); + obj = new_item(); + init_weapon(obj, ARROW); + obj->o_count = rnd(15) + 25; + free(obj); + } + else + { + /* + * Give him some food + */ + obj = new_item(); + obj->o_type = FOOD; + obj->o_count = 1; + add_pack(rs,obj, TRUE); + /* + * And his suit of armor + */ + obj = new_item(); + obj->o_type = ARMOR; + obj->o_which = RING_MAIL; + obj->o_arm = a_class[RING_MAIL] - 1; + obj->o_flags |= ISKNOW; + obj->o_count = 1; + cur_armor = obj; + add_pack(rs,obj, TRUE); + /* + * Give him his weaponry. First a mace. + */ + obj = new_item(); + init_weapon(obj, MACE); + obj->o_hplus = 1; + obj->o_dplus = 1; + obj->o_flags |= ISKNOW; + add_pack(rs,obj, TRUE); + cur_weapon = obj; + /* + * Now a +1 bow + */ + obj = new_item(); + init_weapon(obj, BOW); + obj->o_hplus = 1; + obj->o_flags |= ISKNOW; + add_pack(rs,obj, TRUE); + /* + * Now some arrows + */ + obj = new_item(); + init_weapon(obj, ARROW); + obj->o_count = rnd(15) + 25; + obj->o_flags |= ISKNOW; + add_pack(rs,obj, TRUE); + //fprintf(stderr,"initial o_count.%d\n",obj->o_count); sleep(3); + } +} + +/* + * Contains defintions and functions for dealing with things like + * potions and scrolls + */ + +const char *rainbow[] = { + "amber", + "aquamarine", + "black", + "blue", + "brown", + "clear", + "crimson", + "cyan", + "ecru", + "gold", + "green", + "grey", + "magenta", + "orange", + "pink", + "plaid", + "purple", + "red", + "silver", + "tan", + "tangerine", + "topaz", + "turquoise", + "vermilion", + "violet", + "white", + "yellow", +}; + +#define NCOLORS (sizeof rainbow / sizeof (char *)) +int cNCOLORS = NCOLORS; + +static const char *sylls[] = { + "a", "ab", "ag", "aks", "ala", "an", "app", "arg", "arze", "ash", + "bek", "bie", "bit", "bjor", "blu", "bot", "bu", "byt", "comp", + "con", "cos", "cre", "dalf", "dan", "den", "do", "e", "eep", "el", + "eng", "er", "ere", "erk", "esh", "evs", "fa", "fid", "fri", "fu", + "gan", "gar", "glen", "gop", "gre", "ha", "hyd", "i", "ing", "ip", + "ish", "it", "ite", "iv", "jo", "kho", "kli", "klis", "la", "lech", + "mar", "me", "mi", "mic", "mik", "mon", "mung", "mur", "nej", + "nelg", "nep", "ner", "nes", "nes", "nih", "nin", "o", "od", "ood", + "org", "orn", "ox", "oxy", "pay", "ple", "plu", "po", "pot", + "prok", "re", "rea", "rhov", "ri", "ro", "rog", "rok", "rol", "sa", + "san", "sat", "sef", "seh", "shu", "ski", "sna", "sne", "snik", + "sno", "so", "sol", "sri", "sta", "sun", "ta", "tab", "tem", + "ther", "ti", "tox", "trol", "tue", "turs", "u", "ulk", "um", "un", + "uni", "ur", "val", "viv", "vly", "vom", "wah", "wed", "werg", + "wex", "whon", "wun", "xo", "y", "yot", "yu", "zant", "zeb", "zim", + "zok", "zon", "zum", +}; + +const STONE stones[] = { + { "agate", 25}, + { "alexandrite", 40}, + { "amethyst", 50}, + { "carnelian", 40}, + { "diamond", 300}, + { "emerald", 300}, + { "germanium", 225}, + { "granite", 5}, + { "garnet", 50}, + { "jade", 150}, + { "kryptonite", 300}, + { "lapis lazuli", 50}, + { "moonstone", 50}, + { "obsidian", 15}, + { "onyx", 60}, + { "opal", 200}, + { "pearl", 220}, + { "peridot", 63}, + { "ruby", 350}, + { "sapphire", 285}, + { "stibotantalite", 200}, + { "tiger eye", 50}, + { "topaz", 60}, + { "turquoise", 70}, + { "taaffeite", 300}, + { "zircon", 80}, +}; + +#define NSTONES (sizeof stones / sizeof (STONE)) +int cNSTONES = NSTONES; + +const char *wood[] = { + "avocado wood", + "balsa", + "bamboo", + "banyan", + "birch", + "cedar", + "cherry", + "cinnibar", + "cypress", + "dogwood", + "driftwood", + "ebony", + "elm", + "eucalyptus", + "fall", + "hemlock", + "holly", + "ironwood", + "kukui wood", + "mahogany", + "manzanita", + "maple", + "oaken", + "persimmon wood", + "pecan", + "pine", + "poplar", + "redwood", + "rosewood", + "spruce", + "teak", + "walnut", + "zebrawood", +}; + +#define NWOOD (sizeof wood / sizeof (char *)) +int cNWOOD = NWOOD; + +const char *metal[] = { + "aluminum", + "beryllium", + "bone", + "brass", + "bronze", + "copper", + "electrum", + "gold", + "iron", + "lead", + "magnesium", + "mercury", + "nickel", + "pewter", + "platinum", + "steel", + "silver", + "silicon", + "tin", + "titanium", + "tungsten", + "zinc", +}; + +#define NMETAL (sizeof metal / sizeof (char *)) +int cNMETAL = NMETAL; +#define MAX3(a,b,c) (a > b ? (a > c ? a : c) : (b > c ? b : c)) + +static bool used[MAX3(NCOLORS, NSTONES, NWOOD)]; + +/* + * init_colors: + * Initialize the potion color scheme for this time + */ +void +init_colors() +{ + register int i, j; + memset(used,0,sizeof(used)); + for (i = 0; i < NCOLORS; i++) + used[i] = FALSE; + for (i = 0; i < MAXPOTIONS; i++) + { + do + j = rnd(NCOLORS); + until (!used[j]); + used[j] = TRUE; + p_colors[i] = rainbow[j]; + } +} + +/* + * init_names: + * Generate the names of the various scrolls + */ +#define MAXNAME 40 /* Max number of characters in a name */ + +void +init_names() +{ + register int nsyl; + register char *cp; const char *sp; + register int i, nwords; + + for (i = 0; i < MAXSCROLLS; i++) + { + cp = prbuf; + nwords = rnd(3) + 2; + while (nwords--) + { + nsyl = rnd(3) + 1; + while (nsyl--) + { + sp = sylls[rnd((sizeof sylls) / (sizeof (char *)))]; + if (&cp[strlen(sp)] > &prbuf[MAXNAME]) + break; + while (*sp) + *cp++ = *sp++; + } + *cp++ = ' '; + } + *--cp = '\0'; + s_names[i] = (char *) malloc((unsigned) strlen(prbuf)+1); + strcpy(s_names[i], prbuf); + } +} + +/* + * init_stones: + * Initialize the ring stone setting scheme for this time + */ +void +init_stones() +{ + register int i, j; + for (i = 0; i < NSTONES; i++) + used[i] = FALSE; + for (i = 0; i < MAXRINGS; i++) + { + do + j = rnd(NSTONES); + until (!used[j]); + used[j] = TRUE; + r_stones[i] = stones[j].st_name; + ring_info[i].oi_worth += stones[j].st_value; + } +} + +/* + * init_materials: + * Initialize the construction materials for wands and staffs + */ +void +init_materials() +{ + register int i, j; + register const char *str; + static bool metused[NMETAL]; + memset(metused,0,sizeof(metused)); + for (i = 0; i < NWOOD; i++) + used[i] = FALSE; + for (i = 0; i < NMETAL; i++) + metused[i] = FALSE; + for (i = 0; i < MAXSTICKS; i++) + { + for (;;) + if (rnd(2) == 0) + { + j = rnd(NMETAL); + if (!metused[j]) + { + ws_type[i] = "wand"; + str = metal[j]; + metused[j] = TRUE; + break; + } + } + else + { + j = rnd(NWOOD); + if (!used[j]) + { + ws_type[i] = "staff"; + str = wood[j]; + used[j] = TRUE; + break; + } + } + ws_made[i] = str; + } +} + +#ifdef MASTER +# define NT NUMTHINGS, "things" +# define MP MAXPOTIONS, "potions" +# define MS MAXSCROLLS, "scrolls" +# define MR MAXRINGS, "rings" +# define MWS MAXSTICKS, "sticks" +# define MW MAXWEAPONS, "weapons" +# define MA MAXARMORS, "armor" +#else +# define NT NUMTHINGS +# define MP MAXPOTIONS +# define MS MAXSCROLLS +# define MR MAXRINGS +# define MWS MAXSTICKS +# define MW MAXWEAPONS +# define MA MAXARMORS +#endif + +/* + * sumprobs: + * Sum up the probabilities for items appearing + */ +void +sumprobs(struct obj_info *info, int bound +#ifdef MASTER + , char *name +#endif +) +{ +#ifdef MASTER + struct obj_info *start = info; +#endif + struct obj_info *endp; + endp = info + bound; + while (++info < endp) + info->oi_prob += (info - 1)->oi_prob; +#ifdef MASTER + badcheck(name, start, bound); +#endif +} + +/* + * init_probs: + * Initialize the probabilities for the various items + */ +void +init_probs() +{ + sumprobs(things, NT); + sumprobs(pot_info, MP); + sumprobs(scr_info, MS); + sumprobs(ring_info, MR); + sumprobs(ws_info, MWS); + sumprobs(weap_info, MW); + sumprobs(arm_info, MA); +} + +#ifdef MASTER +/* + * badcheck: + * Check to see if a series of probabilities sums to 100 + */ +void +badcheck(char *name, struct obj_info *info, int bound) +{ + register struct obj_info *end; + + if (info[bound - 1].oi_prob == 100) + return; + printf("\nBad percentages for %s (bound = %d):\n", name, bound); + for (end = &info[bound]; info < end; info++) + printf("%3d%% %s\n", info->oi_prob, info->oi_name); + printf("[hit RETURN to continue]"); + fflush(stdout); + while (getchar() != '\n') + continue; +} +#endif + +/* + * pick_color: + * If he is halucinating, pick a random color name and return it, + * otherwise return the given color. + */ +char * +pick_color(char *col) +{ + return (on(player, ISHALU) ? (char *)rainbow[rnd(NCOLORS)] : col); +} diff --git a/src/cc/rogue/install-sh b/src/cc/rogue/install-sh new file mode 100755 index 000000000..4d4a9519e --- /dev/null +++ b/src/cc/rogue/install-sh @@ -0,0 +1,323 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2005-05-14.22 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# 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 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 +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +chmodcmd="$chmodprog 0755" +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test -n "$1"; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + *) # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + test -n "$dir_arg$dstarg" && break + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done + break;; + esac +done + +if test -z "$1"; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + src= + + if test -d "$dst"; then + mkdircmd=: + chmodcmd= + else + mkdircmd=$mkdirprog + fi + else + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dst=$dst/`basename "$src"` + fi + fi + + # This sed command emulates the dirname command. + dstdir=`echo "$dst" | sed -e 's,/*$,,;s,[^/]*$,,;s,/*$,,;s,^$,.,'` + + # Make sure that the destination directory exists. + + # Skip lots of stat calls in the usual case. + if test ! -d "$dstdir"; then + defaultIFS=' + ' + IFS="${IFS-$defaultIFS}" + + oIFS=$IFS + # Some sh's can't handle IFS=/ for some reason. + IFS='%' + set x `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` + shift + IFS=$oIFS + + pathcomp= + + while test $# -ne 0 ; do + pathcomp=$pathcomp$1 + shift + if test ! -d "$pathcomp"; then + $mkdirprog "$pathcomp" + # mkdir can fail with a `File exist' error in case several + # install-sh are creating the directory concurrently. This + # is OK. + test -d "$pathcomp" || exit + fi + pathcomp=$pathcomp/ + done + fi + + if test -n "$dir_arg"; then + $doit $mkdircmd "$dst" \ + && { test -z "$chowncmd" || $doit $chowncmd "$dst"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dst"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dst"; } + + else + dstfile=`basename "$dst"` + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + trap '(exit $?); exit' 1 2 13 15 + + # Copy the file name to the temp name. + $doit $cpprog "$src" "$dsttmp" && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dstdir/$dstfile" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dstdir/$dstfile"; then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null \ + || $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null \ + || { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + } + } + fi || { (exit 1); exit 1; } +done + +# The final little trick to "correctly" pass the exit status to the exit trap. +{ + (exit 0); exit 0 +} + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/src/cc/rogue/io.c b/src/cc/rogue/io.c new file mode 100644 index 000000000..ad22407f6 --- /dev/null +++ b/src/cc/rogue/io.c @@ -0,0 +1,337 @@ +/* + * Various input/output functions + * + * @(#)io.c 4.32 (Berkeley) 02/05/99 + */ + +//#include +//#include +//#include +//#include + +#include "rogue.h" + +/* + * msg: + * Display a message at the top of the screen. + */ +#define MAXMSG (NUMCOLS - sizeof "--More--") + +static char msgbuf[2*MAXMSG+1]; +static int newpos = 0; + +/* VARARGS1 */ +int +msg(struct rogue_state *rs,char *fmt, ...) +{ + va_list args; + + /* + * if the string is "", just clear the line + */ + if (*fmt == '\0') + { + move(0, 0); + clrtoeol(); + mpos = 0; + return ~ESCAPE; + } + /* + * otherwise add to the message and flush it out + */ + va_start(args, fmt); + doadd(rs,fmt, args); + va_end(args); + return endmsg(rs); +} + +/* + * addmsg: + * Add things to the current message + */ +/* VARARGS1 */ +void +addmsg(struct rogue_state *rs,char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + doadd(rs,fmt, args); + va_end(args); +} + +/* + * endmsg: + * Display a new msg (giving him a chance to see the previous one + * if it is up there with the --More--) + */ +int +endmsg(struct rogue_state *rs) +{ + char ch; + + if (save_msg) + strcpy(huh, msgbuf); + if (mpos) + { + look(rs,FALSE); + mvaddstr(0, mpos, "--More--"); + if ( rs->sleeptime != 0 ) + refresh(); + if (!msg_esc) + wait_for(rs,' '); + else + { + while ((ch = readchar(rs)) != ' ') + if (ch == ESCAPE) + { + msgbuf[0] = '\0'; + mpos = 0; + newpos = 0; + msgbuf[0] = '\0'; + return ESCAPE; + } + } + } + /* + * All messages should start with uppercase, except ones that + * start with a pack addressing character + */ + if (islower(msgbuf[0]) && !lower_msg && msgbuf[1] != ')') + msgbuf[0] = (char) toupper(msgbuf[0]); + mvaddstr(0, 0, msgbuf); + clrtoeol(); + mpos = newpos; + newpos = 0; + msgbuf[0] = '\0'; + if ( rs->sleeptime != 0 ) + refresh(); + return ~ESCAPE; +} + +/* + * doadd: + * Perform an add onto the message buffer + */ +void +doadd(struct rogue_state *rs,char *fmt, va_list args) +{ + static char buf[MAXSTR]; + + /* + * Do the printf into buf + */ + vsprintf(buf, fmt, args); + if (strlen(buf) + newpos >= MAXMSG) + endmsg(rs); + strcat(msgbuf, buf); + newpos = (int) strlen(msgbuf); +} + +/* + * step_ok: + * Returns true if it is ok to step on ch + */ +int +step_ok(int ch) +{ + switch (ch) + { + case ' ': + case '|': + case '-': + return FALSE; + default: + return (!isalpha(ch)); + } +} + +/* + * readchar: + * Reads and returns a character, checking for gross input errors + */ +char +readchar(struct rogue_state *rs) +{ + char c,ch = -1; + if ( rs != 0 && rs->guiflag == 0 ) + { + static uint32_t counter; + if ( rs->ind < rs->numkeys ) + { + c = rs->keystrokes[rs->ind++]; + if ( 0 ) + { + static FILE *fp; static int32_t counter; + if ( fp == 0 ) + fp = fopen("log","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%d: (%c) hp.%d num.%d gold.%d seed.%llu\n",counter,c,pstats.s_hpt,num_packitems(rs),purse,(long long)seed); + fflush(fp); + counter++; + } + } + while ( c == 'Q' && rs->ind < rs->numkeys ) + { + //fprintf(stderr,"Got 'Q' next (%c)\n",rs->keystrokes[rs->ind]); sleep(2); + if ( rs->keystrokes[rs->ind] == 'y' ) + return(c); + rs->ind++; + c = rs->keystrokes[rs->ind++]; + } + return(c); + } + if ( rs->replaydone != 0 && counter++ < 3 ) + fprintf(stderr,"replay finished but readchar called\n"); + rs->replaydone = (uint32_t)time(NULL); + if ( counter < 3 || (counter & 1) == 0 ) + return('y'); + else return(ESCAPE); + } + if ( rs == 0 || rs->guiflag != 0 ) + { + ch = (char) md_readchar(); + + if (ch == 3) + { + _quit(); + return(27); + } + if ( rs != 0 && rs->guiflag != 0 ) + { + if ( rs->num < sizeof(rs->buffered) ) + { + rs->buffered[rs->num++] = ch; + if ( rs->num > (sizeof(rs->buffered)*9)/10 && rs->needflush == 0 ) + { + rs->needflush = (uint32_t)time(NULL); + //fprintf(stderr,"needflush.%u %d of %d\n",rs->needflush,rs->num,(int32_t)sizeof(rs->buffered)); + //sleep(3); + } + } else fprintf(stderr,"buffer filled without flushed\n"); + } + } else fprintf(stderr,"readchar rs.%p non-gui error?\n",rs); + return(ch); +} + +/* + * status: + * Display the important stats line. Keep the cursor where it was. + */ +void +status(struct rogue_state *rs) +{ + register int oy, ox, temp; + static int hpwidth = 0; + static int s_hungry = 0; + static int s_lvl = 0; + static int s_pur = -1; + static int s_hp = 0; + static int s_arm = 0; + static str_t s_str = 0; + static int s_exp = 0; + static char *state_name[] = + { + "", "Hungry", "Weak", "Faint" + }; + + /* + * If nothing has changed since the last status, don't + * bother. + */ + temp = (cur_armor != NULL ? cur_armor->o_arm : pstats.s_arm); + if (s_hp == pstats.s_hpt && s_exp == pstats.s_exp && s_pur == purse + && s_arm == temp && s_str == pstats.s_str && s_lvl == level + && s_hungry == hungry_state + && !stat_msg + ) + return; + + s_arm = temp; + + getyx(stdscr, oy, ox); + if (s_hp != max_hp) + { + temp = max_hp; + s_hp = max_hp; + for (hpwidth = 0; temp; hpwidth++) + temp /= 10; + } + + /* + * Save current status + */ + s_lvl = level; + s_pur = purse; + s_hp = pstats.s_hpt; + s_str = pstats.s_str; + s_exp = pstats.s_exp; + s_hungry = hungry_state; + + if (stat_msg) + { + move(0, 0); + msg(rs,"Level: %d Gold: %-5d Hp: %*d(%*d) Str: %2d(%d) Arm: %-2d Exp: %d/%ld %s", + level, purse, hpwidth, pstats.s_hpt, hpwidth, max_hp, pstats.s_str, + max_stats.s_str, 10 - s_arm, pstats.s_lvl, pstats.s_exp, + state_name[hungry_state]); + } + else + { + move(STATLINE, 0); + + printw("Level: %d Gold: %-5d Hp: %*d(%*d) Str: %2d(%d) Arm: %-2d Exp: %d/%d %s", + level, purse, hpwidth, pstats.s_hpt, hpwidth, max_hp, pstats.s_str, + max_stats.s_str, 10 - s_arm, pstats.s_lvl, pstats.s_exp, + state_name[hungry_state]); + } + + clrtoeol(); + move(oy, ox); +} + +/* + * wait_for + * Sit around until the guy types the right key + */ +void +wait_for(struct rogue_state *rs,int ch) +{ + register char c; + + if (ch == '\n') + while ((c = readchar(rs)) != '\n' && c != '\r') + { + if ( rs->replaydone != 0 ) + return; + continue; + } + else + while (readchar(rs) != ch) + { + if ( rs->replaydone != 0 ) + return; + continue; + } +} + +/* + * show_win: + * Function used to display a window and wait before returning + */ +void +show_win(struct rogue_state *rs,char *message) +{ + WINDOW *win; + + win = hw; + wmove(win, 0, 0); + waddstr(win, message); + touchwin(win); + wmove(win, hero.y, hero.x); + wrefresh(win); + wait_for(rs,' '); + clearok(curscr, TRUE); + touchwin(stdscr); +} diff --git a/src/cc/rogue/list.c b/src/cc/rogue/list.c new file mode 100644 index 000000000..a021c6a8f --- /dev/null +++ b/src/cc/rogue/list.c @@ -0,0 +1,179 @@ +/* + * Functions for dealing with linked lists of goodies + * + * @(#)list.c 4.12 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +#include "rogue.h" + +#ifdef MASTER +int total = 0; /* total dynamic memory bytes */ +#endif + +/* + * discard: + * Free up an item + */ + +//#define ENABLE_DEBUG +#define MAX_DEBUGPTRS 100000 + +int32_t itemcounter; +THING *thingptrs[MAX_DEBUGPTRS]; +int32_t numptrs; + +int32_t thing_find(THING *item) +{ +#ifdef ENABLE_DEBUG + int32_t i; + for (i=0; i_t._t_type,thingptrs[i]->o_type,thingptrs[i]->o_type); + free(thingptrs[i]); + } + memset(thingptrs,0,sizeof(thingptrs)); + numptrs = 0; +} + +/* + * detach: + * takes an item out of whatever linked list it might be in + */ + +void +_detach(THING **list, THING *item) +{ + if (*list == item) + *list = next(item); + if (prev(item) != NULL) + item->l_prev->l_next = next(item); + if (next(item) != NULL) + item->l_next->l_prev = prev(item); + item->l_next = NULL; + item->l_prev = NULL; +} + +/* + * _attach: + * add an item to the head of a list + */ + +void +_attach(THING **list, THING *item) +{ + if (*list != NULL) + { + item->l_next = *list; + (*list)->l_prev = item; + item->l_prev = NULL; + } + else + { + item->l_next = NULL; + item->l_prev = NULL; + } + *list = item; +} + +/* + * _free_list: + * Throw the whole blamed thing away + */ + +void +_free_list(THING **ptr) +{ + THING *item; + while (*ptr != NULL) + { + item = *ptr; + *ptr = next(item); + discard(item); + } +} + +/* + * new_item + * Get a new item with a specified size + */ + +THING * +new_item(void) +{ + THING *item; + +#ifdef MASTER + if ((item = (THING *)calloc(1, sizeof *item)) == NULL) + msg(rs,"ran out of memory after %d items", total); + else + total++; +#else + item = (THING *)calloc(1, sizeof *item); +#endif +#ifdef ENABLE_DEBUG + if ( numptrs < MAX_DEBUGPTRS ) + { + thingptrs[numptrs++] = item; + if ( (++itemcounter % 100) == 0 ) + fprintf(stderr,"itemcounter.%d\n",itemcounter); + } +#endif + item->l_next = NULL; + item->l_prev = NULL; + return item; +} diff --git a/src/cc/rogue/mach_dep.c b/src/cc/rogue/mach_dep.c new file mode 100644 index 000000000..6d0e1c66f --- /dev/null +++ b/src/cc/rogue/mach_dep.c @@ -0,0 +1,459 @@ +/* + * Various installation dependent routines + * + * @(#)mach_dep.c 4.37 (Berkeley) 05/23/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +/* + * The various tuneable defines are: + * + * SCOREFILE Where/if the score file should live. + * ALLSCORES Score file is top ten scores, not top ten + * players. This is only useful when only a few + * people will be playing; otherwise the score file + * gets hogged by just a few people. + * NUMSCORES Number of scores in the score file (default 10). + * NUMNAME String version of NUMSCORES (first character + * should be capitalized) (default "Ten"). + * MAXLOAD What (if any) the maximum load average should be + * when people are playing. Since it is divided + * by 10, to specify a load limit of 4.0, MAXLOAD + * should be "40". If defined, then + * LOADAV Should it use it's own routine to get + * the load average? + * NAMELIST If so, where does the system namelist + * hide? + * MAXUSERS What (if any) the maximum user count should be + * when people are playing. If defined, then + * UCOUNT Should it use it's own routine to count + * users? + * UTMP If so, where does the user list hide? + * CHECKTIME How often/if it should check during the game + * for high load average. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include "rogue.h" +#include "extern.h" + +#define NOOP(x) (x += 0) + +# ifndef NUMSCORES +# define NUMSCORES 10 +# define NUMNAME "Ten" +# endif + +unsigned int numscores = NUMSCORES; +char *Numname = NUMNAME; + +# ifdef ALLSCORES +bool allscore = TRUE; +# else /* ALLSCORES */ +bool allscore = FALSE; +# endif /* ALLSCORES */ + +#ifdef CHECKTIME +static int num_checks; /* times we've gone over in checkout() */ +#endif /* CHECKTIME */ + +/* + * init_check: + * Check out too see if it is proper to play the game now + */ + +void +init_check() +{ +#if defined(MAXLOAD) || defined(MAXUSERS) + if (too_much()) + { + printf("Sorry, %s, but the system is too loaded now.\n", whoami); + printf("Try again later. Meanwhile, why not enjoy a%s %s?\n", + vowelstr(fruit), fruit); + if (author()) + printf("However, since you're a good guy, it's up to you\n"); + else + exit(1); + } +#endif +} + +/* + * open_score: + * Open up the score file for future use + */ + +void +open_score() +{ +#ifdef SCOREFILE + char *scorefile = SCOREFILE; + /* + * We drop setgid privileges after opening the score file, so subsequent + * open()'s will fail. Just reuse the earlier filehandle. + */ + + if (scoreboard != NULL) { + rewind(scoreboard); + return; + } + + scoreboard = fopen(scorefile, "r+"); + + if ((scoreboard == NULL) && (errno == ENOENT)) + { + scoreboard = fopen(scorefile, "w+"); + md_chmod(scorefile,0664); + } + + if (scoreboard == NULL) { + fprintf(stderr, "Could not open %s for writing: %s\n", scorefile, strerror(errno)); + fflush(stderr); + } +#else + scoreboard = NULL; +#endif +} + +/* + * setup: + * Get starting setup for all games + */ + +void +setup() +{ +#ifdef CHECKTIME + int checkout(); +#endif + +#ifdef DUMP + md_onsignal_autosave(); +#else + md_onsignal_default(); +#endif + +#ifdef CHECKTIME + md_start_checkout_timer(CHECKTIME*60); + num_checks = 0; +#endif + + raw(); /* Raw mode */ + noecho(); /* Echo off */ + keypad(stdscr,1); + getltchars(); /* get the local tty chars */ +} + +/* + * getltchars: + * Get the local tty chars for later use + */ + +void +getltchars() +{ + got_ltc = TRUE; + orig_dsusp = md_dsuspchar(); + md_setdsuspchar( md_suspchar() ); +} + +/* + * resetltchars: + * Reset the local tty chars to original values. + */ +void +resetltchars(void) +{ + if (got_ltc) { + md_setdsuspchar(orig_dsusp); + } +} + +/* + * playltchars: + * Set local tty chars to the values we use when playing. + */ +void +playltchars(void) +{ + if (got_ltc) { + md_setdsuspchar( md_suspchar() ); + } +} + +/* + * start_score: + * Start the scoring sequence + */ + +void +start_score() +{ +#ifdef CHECKTIME + md_stop_checkout_timer(); +#endif +} + +/* + * is_symlink: + * See if the file has a symbolic link + */ +bool +is_symlink(char *sp) +{ +#ifdef S_IFLNK + struct stat sbuf2; + + if (lstat(sp, &sbuf2) < 0) + return FALSE; + else + return ((sbuf2.st_mode & S_IFMT) != S_IFREG); +#else + NOOP(sp); + return FALSE; +#endif +} + +#if defined(MAXLOAD) || defined(MAXUSERS) +/* + * too_much: + * See if the system is being used too much for this game + */ +bool +too_much() +{ +#ifdef MAXLOAD + double avec[3]; +#else + int cnt; +#endif + +#ifdef MAXLOAD + md_loadav(avec); + if (avec[1] > (MAXLOAD / 10.0)) + return TRUE; +#endif +#ifdef MAXUSERS + if (ucount() > MAXUSERS) + return TRUE; +#endif + return FALSE; +} + +/* + * author: + * See if a user is an author of the program + */ +bool +author() +{ +#ifdef MASTER + if (wizard) + return TRUE; +#endif + switch (md_getuid()) + { + case -1: + return TRUE; + default: + return FALSE; + } +} +#endif + +#ifdef CHECKTIME +/* + * checkout: + * Check each CHECKTIME seconds to see if the load is too high + */ + +checkout(struct rogue_state *rs,int sig) +{ + static char *msgs[] = { + "The load is too high to be playing. Please leave in %0.1f minutes", + "Please save your game. You have %0.1f minutes", + "Last warning. You have %0.1f minutes to leave", + }; + int checktime; + + if (too_much()) + { + if (author()) + { + num_checks = 1; + chmsg(rs,"The load is rather high, O exaulted one"); + } + else if (num_checks++ == 3) + fatal("Sorry. You took too long. You are dead\n"); + checktime = (CHECKTIME * 60) / num_checks; + chmsg(rs,msgs[num_checks - 1], ((double) checktime / 60.0)); + } + else + { + if (num_checks) + { + num_checks = 0; + chmsg(rs,"The load has dropped back down. You have a reprieve"); + } + checktime = (CHECKTIME * 60); + } + + md_start_checkout_timer(checktime); +} + +/* + * chmsg: + * checkout()'s version of msg. If we are in the middle of a + * shell, do a printf instead of a msg to a the refresh. + */ +/* VARARGS1 */ + +chmsg(struct rogue_state *rs,char *fmt, int arg) +{ + if (!in_shell) + msg(rs,fmt, arg); + else + { + printf(fmt, arg); + putchar('\n'); + fflush(stdout); + } +} +#endif + +#ifdef UCOUNT +/* + * ucount: + * count number of users on the system + */ +#include + +struct utmp buf; + +int +ucount() +{ + struct utmp *up; + FILE *utmp; + int count; + + if ((utmp = fopen(UTMP, "r")) == NULL) + return 0; + + up = &buf; + count = 0; + + while (fread(up, 1, sizeof (*up), utmp) > 0) + if (buf.ut_name[0] != '\0') + count++; + fclose(utmp); + return count; +} +#endif + +/* + * lock_sc: + * lock the score file. If it takes too long, ask the user if + * they care to wait. Return TRUE if the lock is successful. + */ +static FILE *lfd = NULL; +bool +lock_sc() +{ +#if defined(SCOREFILE) && defined(LOCKFILE) + int cnt; + static struct stat sbuf; + char *lockfile = LOCKFILE; + +over: + if ((lfd=fopen(lockfile, "w+")) != NULL) + return TRUE; + for (cnt = 0; cnt < 5; cnt++) + { + md_sleep(1); + if ((lfd=fopen(lockfile, "w+")) != NULL) + return TRUE; + } + if (stat(lockfile, &sbuf) < 0) + { + lfd=fopen(lockfile, "w+"); + return TRUE; + } + if (time(NULL) - sbuf.st_mtime > 10) + { + if (md_unlink(lockfile) < 0) + return FALSE; + goto over; + } + else + { + printf("The score file is very busy. Do you want to wait longer\n"); + printf("for it to become free so your score can get posted?\n"); + printf("If so, type \"y\"\n"); + if (fgets(prbuf, MAXSTR, stdin) != 0 ) + ; + if (prbuf[0] == 'y') + for (;;) + { + if ((lfd=fopen(lockfile, "w+")) != 0) + return TRUE; + if (stat(lockfile, &sbuf) < 0) + { + lfd=fopen(lockfile, "w+"); + return TRUE; + } + if (time(NULL) - sbuf.st_mtime > 10) + { + if (md_unlink(lockfile) < 0) + return FALSE; + } + md_sleep(1); + } + else + return FALSE; + } +#else + return TRUE; +#endif +} + +/* + * unlock_sc: + * Unlock the score file + */ + +void +unlock_sc() +{ +#if defined(SCOREFILE) && defined(LOCKFILE) + if (lfd != NULL) + fclose(lfd); + lfd = NULL; + md_unlink(LOCKFILE); +#endif +} + +/* + * flush_type: + * Flush typeahead for traps, etc. + */ + +void +flush_type() +{ + flushinp(); +} diff --git a/src/cc/rogue/main.c b/src/cc/rogue/main.c new file mode 100644 index 000000000..093b73d70 --- /dev/null +++ b/src/cc/rogue/main.c @@ -0,0 +1,1042 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include + +char USERPASS[8192]; uint16_t ROGUE_PORT; +extern char Gametxidstr[67]; + +#define SMALLVAL 0.000000000000001 +#define SATOSHIDEN ((uint64_t)100000000L) +#define dstr(x) ((double)(x) / SATOSHIDEN) +#define KOMODO_ASSETCHAIN_MAXLEN 65 +char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],IPADDRESS[100]; + +#ifndef _BITS256 +#define _BITS256 +union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; +typedef union _bits256 bits256; +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +int gettimeofday(struct timeval * tp, struct timezone * tzp) +{ + // Note: some broken versions only have 8 trailing zero's, the correct epoch has 9 trailing zero's + static const uint64_t EPOCH = ((uint64_t)116444736000000000ULL); + + SYSTEMTIME system_time; + FILETIME file_time; + uint64_t time; + + GetSystemTime(&system_time); + SystemTimeToFileTime(&system_time, &file_time); + time = ((uint64_t)file_time.dwLowDateTime); + time += ((uint64_t)file_time.dwHighDateTime) << 32; + + tp->tv_sec = (long)((time - EPOCH) / 10000000L); + tp->tv_usec = (long)(system_time.wMilliseconds * 1000); + return 0; +} +#endif // _MSC_VER +#endif + + + +double OS_milliseconds() +{ + struct timeval tv; double millis; + #ifdef __MINGW32__ + mingw_gettimeofday(&tv,NULL); + #else + gettimeofday(&tv,NULL); + #endif + millis = ((double)tv.tv_sec * 1000. + (double)tv.tv_usec / 1000.); + //printf("tv_sec.%ld usec.%d %f\n",tv.tv_sec,tv.tv_usec,millis); + return(millis); +} + +int32_t _unhex(char c) +{ + if ( c >= '0' && c <= '9' ) + return(c - '0'); + else if ( c >= 'a' && c <= 'f' ) + return(c - 'a' + 10); + else if ( c >= 'A' && c <= 'F' ) + return(c - 'A' + 10); + return(-1); +} + +int32_t is_hexstr(char *str,int32_t n) +{ + int32_t i; + if ( str == 0 || str[0] == 0 ) + return(0); + for (i=0; str[i]!=0; i++) + { + if ( n > 0 && i >= n ) + break; + if ( _unhex(str[i]) < 0 ) + break; + } + if ( n == 0 ) + return(i); + return(i == n); +} + +int32_t unhex(char c) +{ + int32_t hex; + if ( (hex= _unhex(c)) < 0 ) + { + //printf("unhex: illegal hexchar.(%c)\n",c); + } + return(hex); +} + +unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } + +int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex) +{ + int32_t adjust,i = 0; + //printf("decode.(%s)\n",hex); + if ( is_hexstr(hex,n) <= 0 ) + { + memset(bytes,0,n); + return(n); + } + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) + { + if ( n > 0 ) + { + bytes[0] = unhex(hex[0]); + printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); + } + bytes++; + hex++; + adjust = 1; + } else adjust = 0; + if ( n > 0 ) + { + for (i=0; i>4) & 0xf); + hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); + //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); + } + hexbytes[len*2] = 0; + //printf("len.%ld\n",len*2+1); + return((int32_t)len*2+1); +} + +char *bits256_str(char hexstr[65],bits256 x) +{ + init_hexbytes_noT(hexstr,x.bytes,sizeof(x)); + return(hexstr); +} + +long _stripwhite(char *buf,int accept) +{ + int32_t i,j,c; + if ( buf == 0 || buf[0] == 0 ) + return(0); + for (i=j=0; buf[i]!=0; i++) + { + buf[j] = c = buf[i]; + if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) + j++; + } + buf[j] = 0; + return(j); +} + +char *clonestr(char *str) +{ + char *clone; + if ( str == 0 || str[0] == 0 ) + { + printf("warning cloning nullstr.%p\n",str); +#ifdef __APPLE__ + while ( 1 ) sleep(1); +#endif + str = (char *)""; + } + clone = (char *)malloc(strlen(str)+16); + strcpy(clone,str); + return(clone); +} + +char *parse_conf_line(char *line,char *field) +{ + line += strlen(field); + for (; *line!='='&&*line!=0; line++) + break; + if ( *line == 0 ) + return(0); + if ( *line == '=' ) + line++; + while ( line[strlen(line)-1] == '\r' || line[strlen(line)-1] == '\n' || line[strlen(line)-1] == ' ' ) + line[strlen(line)-1] = 0; + //printf("LINE.(%s)\n",line); + _stripwhite(line,0); + return(clonestr(line)); +} + +int32_t safecopy(char *dest,char *src,long len) +{ + int32_t i = -1; + if ( src != 0 && dest != 0 && src != dest ) + { + if ( dest != 0 ) + memset(dest,0,len); + for (i=0; i buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +uint8_t *OS_fileptr(long *allocsizep,char *fname) +{ + long filesize = 0; uint8_t *buf = 0; void *retptr; + *allocsizep = 0; + retptr = OS_loadfile(fname,&buf,&filesize,allocsizep); + return((uint8_t *)retptr); +} + +struct MemoryStruct { char *memory; size_t size; }; +struct return_string { char *ptr; size_t len; }; + +// return data from the server +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) + + +/************************************************************************ + * + * Initialize the string handler so that it is thread safe + * + ************************************************************************/ + +void init_string(struct return_string *s) +{ + s->len = 0; + s->ptr = (char *)calloc(1,s->len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr,"init_string malloc() failed\n"); + exit(-1); + } + s->ptr[0] = '\0'; +} + +/************************************************************************ + * + * Use the "writer" to accumulate text until done + * + ************************************************************************/ + +size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s) +{ + size_t new_len = s->len + size*nmemb; + s->ptr = (char *)realloc(s->ptr,new_len+1); + if ( s->ptr == NULL ) + { + fprintf(stderr, "accumulate realloc() failed\n"); + exit(-1); + } + memcpy(s->ptr+s->len,ptr,size*nmemb); + s->ptr[new_len] = '\0'; + s->len = new_len; + return(size * nmemb); +} + +/************************************************************************ + * + * return the current system time in milliseconds + * + ************************************************************************/ + +#define EXTRACT_BITCOIND_RESULT // if defined, ensures error is null and returns the "result" field +#ifdef EXTRACT_BITCOIND_RESULT + +/************************************************************************ + * + * perform post processing of the results + * + ************************************************************************/ + +char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params) +{ + long i,j,len; char *retstr = 0; cJSON *json,*result,*error; + //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr); + return(rpcstr); + } + json = cJSON_Parse(rpcstr); + if ( json == 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params); + free(rpcstr); + return(0); + } + result = cJSON_GetObjectItem(json,"result"); + error = cJSON_GetObjectItem(json,"error"); + if ( error != 0 && result != 0 ) + { + if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL ) + { + retstr = cJSON_Print(result); + len = strlen(retstr); + if ( retstr[0] == '"' && retstr[len-1] == '"' ) + { + for (i=1,j=0; itype&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL ) + { + if ( strcmp(command,"signrawtransaction") != 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr); + } + free(rpcstr); + } else retstr = rpcstr; + free_json(json); + //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr); + return(retstr); +} +#endif + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +/************************************************************************ + * + * perform the query + * + ************************************************************************/ + +char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params) +{ + static int didinit,count,count2; static double elapsedsum,elapsedsum2; + struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle; + char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime; + if ( didinit == 0 ) + { + didinit = 1; + curl_global_init(CURL_GLOBAL_ALL); //init the curl session + } + numretries = 0; + if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") == 0 ) + specialcase = 1; + else specialcase = 0; + if ( url[0] == 0 ) + strcpy(url,"http://127.0.0.1:7876/nxt"); + if ( specialcase != 0 && 0 ) + printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params); +try_again: + if ( retstrp != 0 ) + *retstrp = 0; + starttime = OS_milliseconds(); + curl_handle = curl_easy_init(); + init_string(&s); + headers = curl_slist_append(0,"Expect:"); + + curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER, headers); + curl_easy_setopt(curl_handle,CURLOPT_URL, url); + curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION, (void *)accumulatebytes); // send all data to this function + curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback + curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash + curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback + if ( strncmp(url,"https",5) == 0 ) + { + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); + curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0); + } + if ( userpass != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass); + databuf = 0; + if ( params != 0 ) + { + if ( command != 0 && specialcase == 0 ) + { + len = strlen(params); + if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) { + bracket0 = bracket1 = (char *)""; + } + else + { + bracket0 = (char *)"["; + bracket1 = (char *)"]"; + } + + databuf = (char *)malloc(256 + strlen(command) + strlen(params)); + sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1); + //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf); + // + } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params); + curl_easy_setopt(curl_handle,CURLOPT_POST,1L); + if ( databuf != 0 ) + curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf); + else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params); + } + //laststart = milliseconds(); + res = curl_easy_perform(curl_handle); + curl_slist_free_all(headers); + curl_easy_cleanup(curl_handle); + if ( databuf != 0 ) // clean up temporary buffer + { + free(databuf); + databuf = 0; + } + if ( res != CURLE_OK ) + { + numretries++; + if ( specialcase != 0 ) + { + printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); + free(s.ptr); + return(0); + } + else if ( numretries >= 1 ) + { + //printf("Maximum number of retries exceeded!\n"); + free(s.ptr); + return(0); + } + if ( (rand() % 1000) == 0 ) + printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries); + free(s.ptr); + sleep((1< (%s)\n",params,s.ptr); + count2++; + elapsedsum2 += (OS_milliseconds() - starttime); + if ( (count2 % 10000) == 0) + printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command); + return(s.ptr); + } + } + printf("bitcoind_RPC: impossible case\n"); + free(s.ptr); + return(0); +} + +static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data) +{ + size_t realsize = (size * nmemb); + struct MemoryStruct *mem = (struct MemoryStruct *)data; + mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1)); + if ( mem->memory != 0 ) + { + if ( ptr != 0 ) + memcpy(&(mem->memory[mem->size]),ptr,realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + } + //printf("got %d bytes\n",(int32_t)(size*nmemb)); + return(realsize); +} + +char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3) +{ + struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0; + if ( (cHandle= *cHandlep) == NULL ) + *cHandlep = cHandle = curl_easy_init(); + else curl_easy_reset(cHandle); + //#ifdef DEBUG + //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1); + //#endif + curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )"); + curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0); + //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1); + curl_easy_setopt(cHandle,CURLOPT_URL,url); + curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10); + if ( userpass != 0 && userpass[0] != 0 ) + curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass); + if ( postfields != 0 && postfields[0] != 0 ) + { + curl_easy_setopt(cHandle,CURLOPT_POST,1); + curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields); + } + if ( hdr0 != NULL && hdr0[0] != 0 ) + { + //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:""); + headers = curl_slist_append(headers,hdr0); + if ( hdr1 != 0 && hdr1[0] != 0 ) + headers = curl_slist_append(headers,hdr1); + if ( hdr2 != 0 && hdr2[0] != 0 ) + headers = curl_slist_append(headers,hdr2); + if ( hdr3 != 0 && hdr3[0] != 0 ) + headers = curl_slist_append(headers,hdr3); + } //headers = curl_slist_append(0,"Expect:"); + if ( headers != 0 ) + curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers); + //res = curl_easy_perform(cHandle); + memset(&chunk,0,sizeof(chunk)); + curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback); + curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk); + curl_easy_perform(cHandle); + curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code); + if ( headers != 0 ) + curl_slist_free_all(headers); + if ( code != 200 ) + printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory); + return(chunk.memory); +} + +uint16_t _komodo_userpass(char *username, char *password, FILE *fp) +{ + char *rpcuser,*rpcpassword,*str,*ipaddress,line[8192]; uint16_t port = 0; + rpcuser = rpcpassword = 0; + username[0] = password[0] = 0; + while ( fgets(line,sizeof(line),fp) != 0 ) + { + if ( line[0] == '#' ) + continue; + //printf("line.(%s) %p %p\n",line,strstr(line,(char *)"rpcuser"),strstr(line,(char *)"rpcpassword")); + if ( (str= strstr(line,(char *)"rpcuser")) != 0 ) + rpcuser = parse_conf_line(str,(char *)"rpcuser"); + else if ( (str= strstr(line,(char *)"rpcpassword")) != 0 ) + rpcpassword = parse_conf_line(str,(char *)"rpcpassword"); + else if ( (str= strstr(line,(char *)"rpcport")) != 0 ) + { + port = atoi(parse_conf_line(str,(char *)"rpcport")); + //fprintf(stderr,"rpcport.%u in file\n",port); + } + else if ( (str= strstr(line,(char *)"ipaddress")) != 0 ) + { + ipaddress = parse_conf_line(str,(char *)"ipaddress"); + strcpy(IPADDRESS,ipaddress); + } + } + if ( rpcuser != 0 && rpcpassword != 0 ) + { + strcpy(username,rpcuser); + strcpy(password,rpcpassword); + } + //printf("rpcuser.(%s) rpcpassword.(%s) %u ipaddress.%s\n",rpcuser,rpcpassword,port,ipaddress); + if ( rpcuser != 0 ) + free(rpcuser); + if ( rpcpassword != 0 ) + free(rpcpassword); + return(port); +} + +/*void komodo_statefname(char *fname,char *symbol,char *str) +{ + int32_t n,len; + sprintf(fname,"%s",getDataDir()); + if ( (n= (int32_t)strlen(ASSETCHAINS_SYMBOL)) != 0 ) + { + len = (int32_t)strlen(fname); + if ( strcmp(ASSETCHAINS_SYMBOL,&fname[len - n]) == 0 ) + fname[len - n] = 0; + else + { + printf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,ASSETCHAINS_SYMBOL,n,len,&fname[len - n]); + return; + } + } + else + { +#ifdef _WIN32 + strcat(fname,"\\"); +#else + strcat(fname,"/"); +#endif + } + if ( symbol != 0 && symbol[0] != 0 && strcmp("KMD",symbol) != 0 ) + { + strcat(fname,symbol); + //printf("statefname.(%s) -> (%s)\n",symbol,fname); +#ifdef _WIN32 + strcat(fname,"\\"); +#else + strcat(fname,"/"); +#endif + } + strcat(fname,str); + //printf("test.(%s) -> [%s] statename.(%s) %s\n",test,ASSETCHAINS_SYMBOL,symbol,fname); +}*/ + +uint16_t komodo_userpass(char *userpass,char *symbol) +{ + FILE *fp; uint16_t port = 0; char fname[512],username[512],password[512],confname[KOMODO_ASSETCHAIN_MAXLEN]; + userpass[0] = 0; + if ( strcmp("KMD",symbol) == 0 ) + { +#ifdef __APPLE__ + sprintf(confname,"Komodo.conf"); +#else + sprintf(confname,"komodo.conf"); +#endif + } + else sprintf(confname,"%s.conf",symbol); + //komodo_statefname(fname,symbol,confname); + if ( (fp= fopen(confname,"rb")) != 0 ) + { + port = _komodo_userpass(username,password,fp); + sprintf(userpass,"%s:%s",username,password); + if ( strcmp(symbol,ASSETCHAINS_SYMBOL) == 0 ) + strcpy(USERPASS,userpass); + fclose(fp); + } + return(port); +} + +#define is_cJSON_True(json) ((json) != 0 && ((json)->type & 0xff) == cJSON_True) + +char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) +{ + //static void *cHandle; + char url[512],*retstr=0,*retstr2=0,postdata[8192]; + if ( params == 0 || params[0] == 0 ) + params = (char *)"[]"; + if ( strlen(params) < sizeof(postdata)-128 ) + { + sprintf(url,(char *)"http://%s:%u",IPADDRESS,port); + sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); + //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,USERPASS); + retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); + //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); + } + return(retstr2); +} + +#include "rogue.h" + +int32_t rogue_sendrawtransaction(char *rawtx) +{ + char *params,*retstr,*hexstr; cJSON *retjson,*resobj; int32_t retval = -1; + params = (char *)malloc(strlen(rawtx) + 16); + sprintf(params,"[\"%s\"]",rawtx); + if ( (retstr= komodo_issuemethod(USERPASS,"sendrawtransaction",params,ROGUE_PORT)) != 0 ) + { + if ( 0 ) // causes 4th level crash + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("rogue.sendlog","wb"); + if ( fp != 0 ) + { + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,"result")) != 0 ) + { + if ( (hexstr= jstr(resobj,0)) != 0 && is_hexstr(hexstr,64) == 64 ) + retval = 0; + } + free_json(retjson); + } + + /* log sendrawtx result in file */ + + /* + FILE *debug_file; + debug_file = fopen("tx_debug.log", "a"); + fprintf(debug_file, "%s\n", retstr); + fflush(debug_file); + fclose(debug_file); + */ + + free(retstr); + } + free(params); + return(retval); +} + +int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +{ + char cmd[16384],hexstr[16384],params[32768],*retstr,*errstr,*rawtx,*pastkeys,*pastcmp,*keys; int32_t i,len,numpastkeys,retflag = -1; cJSON *retjson,*resobj; + //fprintf(stderr,"rogue_progress num.%d\n",num); + if ( rs->guiflag != 0 && Gametxidstr[0] != 0 ) + { + if ( rs->keystrokeshex != 0 ) + { + if ( rogue_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + if ( waitflag == 0 ) + return(0); + else if ( 0 ) + { + while ( rogue_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"pre-rebroadcast\n"); + sleep(10); + } + } + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + if ( 0 && (pastkeys= rogue_keystrokesload(&numpastkeys,seed,1)) != 0 ) + { + sprintf(params,"[\"extract\",\"17\",\"[%%22%s%%22]\"]",Gametxidstr); + if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,ROGUE_PORT)) != 0 ) + { + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,"result")) != 0 && (keys= jstr(resobj,"keystrokes")) != 0 ) + { + len = strlen(keys) / 2; + pastcmp = (char *)malloc(len + 1); + decode_hex(pastcmp,len,keys); + fprintf(stderr,"keystrokes.(%s) vs pastkeys\n",keys); + for (i=0; i> keystrokes.log",Gametxidstr,hexstr); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + } + else + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("keystrokes.log","a"); + sprintf(params,"[\"keystrokes\",\"17\",\"[%%22%s%%22,%%22%s%%22]\"]",Gametxidstr,hexstr); + if ( (retstr= komodo_issuemethod(USERPASS,"cclib",params,ROGUE_PORT)) != 0 ) + { + if ( fp != 0 ) + { + fprintf(fp,"%s\n",params); + fprintf(fp,"%s\n",retstr); + fflush(fp); + } + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( (resobj= jobj(retjson,"result")) != 0 && (rawtx= jstr(resobj,"hex")) != 0 ) + { + if ( rs->keystrokeshex != 0 ) + free(rs->keystrokeshex); + if ( (errstr= jstr(resobj,"error")) == 0 ) + { + rs->keystrokeshex = (char *)malloc(strlen(rawtx)+1); + strcpy(rs->keystrokeshex,rawtx); + retflag = 1; + } else fprintf(stderr,"error sending keystrokes tx\n"), sleep(1); +//fprintf(stderr,"set keystrokestx <- %s\n",rs->keystrokeshex); + } + free_json(retjson); + } + free(retstr); + } + if ( 0 && waitflag != 0 && rs->keystrokeshex != 0 ) + { + while ( rogue_sendrawtransaction(rs->keystrokeshex) == 0 ) + { + //fprintf(stderr,"post-rebroadcast\n"); + sleep(3); + } + free(rs->keystrokeshex), rs->keystrokeshex = 0; + } + } + } + return(retflag); +} + +int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr) +{ + char cmd[32768]; int32_t i,n,retval=-1; char params[1024],*filestr=0,*pname,*statusstr,*datastr,fname[128]; long allocsize; cJSON *retjson,*array,*item,*resultjson; + if ( rs->guiflag == 0 ) + return(-1); + if ( gametxidstr == 0 || *gametxidstr == 0 ) + return(retval); + if ( 0 ) + { + sprintf(fname,"%s.gameinfo",gametxidstr); + sprintf(cmd,"./komodo-cli -ac_name=ROGUE cclib gameinfo 17 \\\"[%%22%s%%22]\\\" > %s",gametxidstr,fname); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd); + else filestr = (char *)OS_fileptr(&allocsize,fname); + } + else + { + sprintf(params,"[\"gameinfo\",\"17\",\"[%%22%s%%22]\"]",gametxidstr); + filestr = komodo_issuemethod(USERPASS,"cclib",params,ROGUE_PORT); + } + if ( filestr != 0 ) + { + if ( (retjson= cJSON_Parse(filestr)) != 0 && (resultjson= jobj(retjson,"result")) != 0 ) + { + //fprintf(stderr,"gameinfo.(%s)\n",jprint(resultjson,0)); + if ( (array= jarray(&n,resultjson,"players")) != 0 ) + { + for (i=0; iP,(int32_t)strlen(datastr)/2,datastr); + fprintf(stderr,"set pname[%s] %s\n",pname==0?"":pname,jprint(item,0)); + rs->restoring = 1; + } + } + } + } + } + free_json(retjson); + } + free(filestr); + } + return(retval); +} + +#ifdef _WIN32 +#ifdef _MSC_VER +__inline int msver(void) { + switch (_MSC_VER) { + case 1500: return 2008; + case 1600: return 2010; + case 1700: return 2012; + case 1800: return 2013; + case 1900: return 2015; + //case 1910: return 2017; + default: return (_MSC_VER / 100); + } +} + +static inline bool is_x64(void) { +#if defined(__x86_64__) || defined(_WIN64) || defined(__aarch64__) + return 1; +#elif defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_IA64) + return 1; +#else + return 0; +#endif +} + +#define BUILD_DATE __DATE__ " " __TIME__ +#endif // _WIN32 +#endif // _MSC_VER + +int main(int argc, char **argv, char **envp) +{ + uint64_t seed; FILE *fp = 0; int32_t i,j,c; char userpass[8192]; + + #ifdef _WIN32 + #ifdef _MSC_VER + printf("*** rogue for Windows [ Build %s ] ***\n", BUILD_DATE); + const char* arch = is_x64() ? "64-bits" : "32-bits"; + printf(" Built with VC++ %d (%ld) %s\n\n", msver(), _MSC_FULL_VER, arch); + #endif + #endif + + for (i=j=0; argv[0][i]!=0&&j +#include + +#if defined(_WIN32) +#include +#include +#include +#include +#pragma warning( disable: 4201 ) +#include +#pragma warning( default: 4201 ) +#include +#undef MOUSE_MOVED +#endif + +//#include +#include "extern.h" + +#if defined(HAVE_SYS_TYPES) +#include +#endif + +#if defined(HAVE_PROCESS_H) +#include +#endif + +#if defined(HAVE_PWD_H) +#include +#endif + +#if defined(HAVE_SYS_UTSNAME) +#include +#endif + +#if defined(HAVE_ARPA_INET_H) +#include /* Solaris 2.8 required this for htonl() and ntohl() */ +#endif + +#if defined(HAVE_TERMIOS_H) +#include +#endif + +#if defined(HAVE_UNISTD_H) +#ifndef __USE_GNU +#define __USE_GNU +#include +#undef __USE_GNU +#else +#include +#endif +#endif + +#ifndef BUILD_ROGUE +#include /* AIX requires curses.h be included before term.h */ +#else +#include "cursesd.h" +#endif + + +#if defined(HAVE_TERM_H) +#include +#elif defined(HAVE_NCURSES_TERM_H) +#include +#endif + +#if defined(HAVE_WORKING_FORK) +#include +#endif + +#include +#include +#include +#include +#include +#include "extern.h" + +#if !defined(PATH_MAX) && defined(_MAX_PATH) +#define PATH_MAX _MAX_PATH +#endif + +#if !defined(PATH_MAX) && defined(_PATH_MAX) +#define PATH_MAX _PATH_MAX +#endif + +#define NOOP(x) (x += 0) + +void +md_init() +{ +#if defined(__INTERIX) + char *term; + + term = getenv("TERM"); + + if (term == NULL) + setenv("TERM","interix"); +#elif defined(__DJGPP__) + _fmode = _O_BINARY; +#elif defined(_WIN32) + _fmode = _O_BINARY; +#endif + +#if defined(HAVE_ESCDELAY) || defined(NCURSES_VERSION) + ESCDELAY=64; +#endif + +#if defined(DUMP) + md_onsignal_default(); +#else + md_onsignal_exit(); +#endif +} + +void +md_onsignal_default() +{ +#ifdef SIGHUP + signal(SIGHUP, SIG_DFL); +#endif +#ifdef SIGQUIT + signal(SIGQUIT, SIG_DFL); +#endif +#ifdef SIGILL + signal(SIGILL, SIG_DFL); +#endif +#ifdef SIGTRAP + signal(SIGTRAP, SIG_DFL); +#endif +#ifdef SIGIOT + signal(SIGIOT, SIG_DFL); +#endif +#ifdef SIGEMT + signal(SIGEMT, SIG_DFL); +#endif +#ifdef SIGFPE + signal(SIGFPE, SIG_DFL); +#endif +#ifdef SIGBUS + signal(SIGBUS, SIG_DFL); +#endif +#ifdef SIGSEGV + signal(SIGSEGV, SIG_DFL); +#endif +#ifdef SIGSYS + signal(SIGSYS, SIG_DFL); +#endif +#ifdef SIGTERM + signal(SIGTERM, SIG_DFL); +#endif +} + +void +md_onsignal_exit() +{ +#ifdef SIGHUP + signal(SIGHUP, SIG_DFL); +#endif +#ifdef SIGQUIT + signal(SIGQUIT, exit); +#endif +#ifdef SIGILL + signal(SIGILL, exit); +#endif +#ifdef SIGTRAP + signal(SIGTRAP, exit); +#endif +#ifdef SIGIOT + signal(SIGIOT, exit); +#endif +#ifdef SIGEMT + signal(SIGEMT, exit); +#endif +#ifdef SIGFPE + signal(SIGFPE, exit); +#endif +#ifdef SIGBUS + signal(SIGBUS, exit); +#endif +#ifdef SIGSEGV + signal(SIGSEGV, exit); +#endif +#ifdef SIGSYS + signal(SIGSYS, exit); +#endif +#ifdef SIGTERM + signal(SIGTERM, exit); +#endif +} + +void +md_onsignal_autosave() +{ +#ifdef SIGHUP + signal(SIGHUP, auto_save); +#endif +#ifdef SIGQUIT + signal(SIGQUIT, endit); +#endif +#ifdef SIGILL + signal(SIGILL, auto_save); +#endif +#ifdef SIGTRAP + signal(SIGTRAP, auto_save); +#endif +#ifdef SIGIOT + signal(SIGIOT, auto_save); +#endif +#ifdef SIGEMT + signal(SIGEMT, auto_save); +#endif +#ifdef SIGFPE + signal(SIGFPE, auto_save); +#endif +#ifdef SIGBUS + signal(SIGBUS, auto_save); +#endif +#ifdef SIGSEGV + signal(SIGSEGV, auto_save); +#endif +#ifdef SIGSYS + signal(SIGSYS, auto_save); +#endif +#ifdef SIGTERM + signal(SIGTERM, auto_save); +#endif +#ifdef SIGINT + signal(SIGINT, quit); +#endif +} + +int +md_hasclreol() +{ +/*#if defined(clr_eol) +#ifdef NCURSES_VERSION + if (cur_term == NULL) + return(0); + //if (cur_term->type.Strings == NULL) + return(0); +#endif + return((clr_eol != NULL) && (*clr_eol != 0)); +#elif defined(__PDCURSES__) + return(TRUE); +#else + return((CE != NULL) && (*CE != 0)); +#endif*/ + return(0); +} + +void +md_putchar(int c) +{ + putchar(c); +} + +#ifdef _WIN32 +static int md_standout_mode = 0; +#endif + +void +md_raw_standout() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + HANDLE hStdout; + WORD fgattr,bgattr; + + if (md_standout_mode == 0) + { + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hStdout, &csbiInfo); + fgattr = (csbiInfo.wAttributes & 0xF); + bgattr = (csbiInfo.wAttributes & 0xF0); + SetConsoleTextAttribute(hStdout,(fgattr << 4) | (bgattr >> 4)); + md_standout_mode = 1; + } +#elif defined(SO) + tputs(SO,0,md_putchar); + fflush(stdout); +#endif +} + +void +md_raw_standend() +{ +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbiInfo; + HANDLE hStdout; + WORD fgattr,bgattr; + + if (md_standout_mode == 1) + { + hStdout = GetStdHandle(STD_OUTPUT_HANDLE); + GetConsoleScreenBufferInfo(hStdout, &csbiInfo); + fgattr = (csbiInfo.wAttributes & 0xF); + bgattr = (csbiInfo.wAttributes & 0xF0); + SetConsoleTextAttribute(hStdout,(fgattr << 4) | (bgattr >> 4)); + md_standout_mode = 0; + } +#elif defined(SE) + tputs(SE,0,md_putchar); + fflush(stdout); +#endif +} + +int +md_unlink_open_file(char *file, FILE *inf) +{ +#ifdef _WIN32 + fclose(inf); + _chmod(file, 0600); + return( _unlink(file) ); +#else + return(unlink(file)); +#endif +} + +int +md_unlink(char *file) +{ +#ifdef _WIN32 + _chmod(file, 0600); + return( _unlink(file) ); +#else + return(unlink(file)); +#endif +} + +int +md_chmod(char *filename, int mode) +{ +#ifdef _WIN32 + return( _chmod(filename, mode) ); +#else + return( chmod(filename, mode) ); +#endif +} + +void +md_normaluser() +{ +#if defined(HAVE_GETGID) && defined(HAVE_GETUID) + gid_t realgid = getgid(); + uid_t realuid = getuid(); + +#if defined(HAVE_SETRESGID) + if (setresgid(-1, realgid, realgid) != 0) { +#elif defined (HAVE_SETREGID) + if (setregid(realgid, realgid) != 0) { +#elif defined (HAVE_SETGID) + if (setgid(realgid) != 0) { +#else + if (0) { +#endif + perror("Could not drop setgid privileges. Aborting."); + exit(1); + } + +#if defined(HAVE_SETRESUID) + if (setresuid(-1, realuid, realuid) != 0) { +#elif defined(HAVE_SETREUID) + if (setreuid(realuid, realuid) != 0) { +#elif defined(HAVE_SETUID) + if (setuid(realuid) != 0) { +#else + if (0) { +#endif + perror("Could not drop setuid privileges. Aborting."); + exit(1); + } +#endif +} + +int +md_getuid() +{ +#ifdef HAVE_GETUID + return( getuid() ); +#else + return(42); +#endif +} + +int +md_getpid() +{ +#ifdef _WIN32 + return( _getpid() ); +#else + return( getpid() ); +#endif +} + +char * +md_getusername() +{ + static char login[80]; + char *l = NULL; +#ifdef _WIN32 + LPSTR mybuffer; + DWORD size = UNLEN + 1; + TCHAR buffer[UNLEN + 1]; + + mybuffer = buffer; + GetUserName(mybuffer,&size); + l = mybuffer; +#elif defined(HAVE_GETPWUID)&& !defined(__DJGPP__) + struct passwd *pw; + + pw = getpwuid(getuid()); + + l = pw->pw_name; +#endif + + if ((l == NULL) || (*l == '\0')) + if ( (l = getenv("USERNAME")) == NULL ) + if ( (l = getenv("LOGNAME")) == NULL ) + if ( (l = getenv("USER")) == NULL ) + l = "nobody"; + + strncpy(login,l,80); + login[79] = 0; + + return(login); +} + +char * +md_gethomedir() +{ + static char homedir[PATH_MAX]; + char *h = NULL; + size_t len; +#if defined(_WIN32) + TCHAR szPath[PATH_MAX]; +#endif +#if defined(_WIN32) || defined(DJGPP) + char slash = '\\'; +#else + char slash = '/'; + struct passwd *pw; + pw = getpwuid(getuid()); + + h = pw->pw_dir; + + if (strcmp(h,"/") == 0) + h = NULL; +#endif + homedir[0] = 0; +#ifdef _WIN32 + if(SUCCEEDED(SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, 0, szPath))) + h = szPath; +#endif + + if ( (h == NULL) || (*h == '\0') ) + { + if ( (h = getenv("HOME")) == NULL ) + { + if ( (h = getenv("HOMEDRIVE")) == NULL) + h = ""; + else + { + strncpy(homedir,h,PATH_MAX-1); + homedir[PATH_MAX-1] = 0; + + if ( (h = getenv("HOMEPATH")) == NULL) + h = ""; + } + } + } + + + len = strlen(homedir); + strncat(homedir,h,PATH_MAX-len-1); + len = strlen(homedir); + + if ((len > 0) && (homedir[len-1] != slash)) { + homedir[len] = slash; + homedir[len+1] = 0; + } + + return(homedir); +} + +void +md_sleep(int s) +{ +#ifdef _WIN32 + Sleep(s); +#else + sleep(s); +#endif +} + +char * +md_getshell() +{ + static char shell[PATH_MAX]; + char *s = NULL; +#ifdef _WIN32 + char *def = "C:\\WINDOWS\\SYSTEM32\\CMD.EXE"; +#elif defined(__DJGPP__) + char *def = "C:\\COMMAND.COM"; +#else + char *def = "/bin/sh"; + struct passwd *pw; + pw = getpwuid(getuid()); + s = pw->pw_shell; +#endif + if ((s == NULL) || (*s == '\0')) + if ( (s = getenv("COMSPEC")) == NULL) + if ( (s = getenv("SHELL")) == NULL) + if ( (s = getenv("SystemRoot")) == NULL) + s = def; + + strncpy(shell,s,PATH_MAX); + shell[PATH_MAX-1] = 0; + + return(shell); +} + +int +md_shellescape() +{ +#if defined(HAVE_WORKING_FORK) + int ret_status; + int pid; + void (*myquit)(int); + void (*myend)(int); + char *sh; + + sh = md_getshell(); + + while((pid = fork()) < 0) + sleep(1); + + if (pid == 0) /* Shell Process */ + { + /* + * Set back to original user, just in case + */ + md_normaluser(); + execl(sh == NULL ? "/bin/sh" : sh, "shell", "-i", NULL); + perror("No shelly"); + _exit(-1); + } + else /* Application */ + { + myend = signal(SIGINT, SIG_IGN); +#ifdef SIGQUIT + myquit = signal(SIGQUIT, SIG_IGN); +#endif + while (wait(&ret_status) != pid) + continue; + + signal(SIGINT, myquit); +#ifdef SIGQUIT + signal(SIGQUIT, myend); +#endif + } + return(ret_status); +#elif defined(HAVE__SPAWNL) + return((int)_spawnl(_P_WAIT,md_getshell(),"shell",NULL,0)); +#elif defined(HAVE_SPAWNL) + return ( spawnl(P_WAIT,md_getshell(),"shell",NULL,0) ); +#else + return(0); +#endif +} + +int +directory_exists(char *dirname) +{ + struct stat sb; + + if (stat(dirname, &sb) == 0) /* path exists */ + return (sb.st_mode & S_IFDIR); + + return(0); +} + +char * +md_getrealname(int uid) +{ + static char uidstr[20]; +#if !defined(_WIN32) && !defined(DJGPP) + struct passwd *pp; + + if ((pp = getpwuid(uid)) == NULL) + { + sprintf(uidstr,"%d", uid); + return(uidstr); + } + else + return(pp->pw_name); +#else + sprintf(uidstr,"%d", uid); + return(uidstr); +#endif +} + +extern char *xcrypt(char *key, char *salt); + +char * +md_crypt(char *key, char *salt) +{ + return( xcrypt(key,salt) ); +} + +char * +md_getpass(char *prompt) +{ +#ifndef HAVE_GETPASS + static char password_buffer[9]; + char *p = password_buffer; + int c, count = 0; + int max_length = 9; + + fflush(stdout); + /* If we can't prompt, abort */ + if (fputs(prompt, stderr) < 0) + { + *p = '\0'; + return NULL; + } + + for(;;) + { + /* Get a character with no echo */ + c = _getch(); + + /* Exit on interrupt (^c or ^break) */ + if (c == '\003' || c == 0x100) + exit(1); + + /* Terminate on end of line or file (^j, ^m, ^d, ^z) */ + if (c == '\r' || c == '\n' || c == '\004' || c == '\032') + break; + + /* Back up on backspace */ + if (c == '\b') + { + if (count) + count--; + else if (p > password_buffer) + p--; + continue; + } + + /* Ignore DOS extended characters */ + if ((c & 0xff) != c) + continue; + + /* Add to password if it isn't full */ + if (p < password_buffer + max_length - 1) + *p++ = (char) c; + else + count++; + } + *p = '\0'; + + fputc('\n', stderr); + + return password_buffer; +#else + return( (char *) getpass(prompt) ); +#endif +} + +int +md_erasechar() +{ +#ifdef HAVE_ERASECHAR + return( erasechar() ); /* process erase character */ +#elif defined(VERASE) + return(_tty.c_cc[VERASE]); /* process erase character */ +#else + #ifndef __MINGW32__ + return(_tty.sg_erase); /* process erase character */ + #endif +#endif +} + +int +md_killchar() +{ +#ifdef HAVE_KILLCHAR + return( killchar() ); +#elif defined(VKILL) + return(_tty.c_cc[VKILL]); +#else + #ifndef __MINGW32__ + return(_tty.sg_kill); + #endif +#endif +} + +int +md_dsuspchar() +{ +#if defined(VDSUSP) /* POSIX has priority */ + struct termios attr; + tcgetattr(STDIN_FILENO, &attr); + return( attr.c_cc[VDSUSP] ); +#elif defined(TIOCGLTC) + struct ltchars ltc; + ioctl(1, TIOCGLTC, <c); + return(ltc.t_dsuspc); +#elif defined(_POSIX_VDISABLE) + return(_POSIX_VDISABLE); +#else + return(0); +#endif +} + +int +md_setdsuspchar(int c) +{ +#if defined(VDSUSP) /* POSIX has priority */ + struct termios attr; + tcgetattr(STDIN_FILENO, &attr); + attr.c_cc[VDSUSP] = c; + tcgetattr(STDIN_FILENO, &attr); +#elif defined(TIOCSLTC) + struct ltchars ltc; + ioctl(1, TIOCGLTC, <c); + ltc.t_dsuspc = c; + ioctl(1, TIOCSLTC, <c); +#else + NOOP(c); +#endif + return(0); +} + +int +md_suspchar() +{ +#if defined(VSUSP) /* POSIX has priority */ + struct termios attr; + tcgetattr(STDIN_FILENO, &attr); + return( attr.c_cc[VSUSP] ); +#elif defined(TIOCGLTC) + struct ltchars ltc; + ioctl(1, TIOCGLTC, <c); + return(ltc.t_suspc); +#elif defined(_POSIX_VDISABLE) + return(_POSIX_VDISABLE); +#else + return(0); +#endif +} + +int +md_setsuspchar(int c) +{ +#if defined(VSUSP) /* POSIX has priority */ + struct termios attr; + tcgetattr(STDIN_FILENO, &attr); + attr.c_cc[VSUSP] = c; + tcgetattr(STDIN_FILENO, &attr); +#elif defined(TIOCSLTC) + struct ltchars ltc; + ioctl(1, TIOCGLTC, <c); + ltc.t_suspc = c; + ioctl(1, TIOCSLTC, <c); +#else + NOOP(c); +#endif + + return(0); +} + +/* + Cursor/Keypad Support + + Sadly Cursor/Keypad support is less straightforward than it should be. + + The various terminal emulators/consoles choose to differentiate the + cursor and keypad keys (with modifiers) in different ways (if at all!). + Furthermore they use different code set sequences for each key only + a subset of which the various curses libraries recognize. Partly due + to incomplete termcap/terminfo entries and partly due to inherent + limitations of those terminal capability databases. + + I give curses first crack at decoding the sequences. If it fails to decode + it we check for common ESC-prefixed sequences. + + All cursor/keypad results are translated into standard rogue movement + commands. + + Unmodified keys are translated to walk commands: hjklyubn + Modified (shift,control,alt) are translated to run commands: HJKLYUBN + + Console and supported (differentiated) keys + Interix: Cursor Keys, Keypad, Ctl-Keypad + Cygwin: Cursor Keys, Keypad, Alt-Cursor Keys + MSYS: Cursor Keys, Keypad, Ctl-Cursor Keys, Ctl-Keypad + Win32: Cursor Keys, Keypad, Ctl/Shift/Alt-Cursor Keys, Ctl/Alt-Keypad + DJGPP: Cursor Keys, Keypad, Ctl/Shift/Alt-Cursor Keys, Ctl/Alt-Keypad + + Interix Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC [D, ESC F^, ESC [D, ESC [D /# Left #/ + ESC [C, ESC F$, ESC [C, ESC [C /# Right #/ + ESC [A, ESC F-, local win, ESC [A /# Up #/ + ESC [B, ESC F+, local win, ESC [B /# Down #/ + ESC [H, ESC [H, ESC [H, ESC [H /# Home #/ + ESC [S, local win, ESC [S, ESC [S /# Page Up #/ + ESC [T, local win, ESC [T, ESC [T /# Page Down #/ + ESC [U, ESC [U, ESC [U, ESC [U /# End #/ + ESC [D, ESC F^, ESC [D, O /# Keypad Left #/ + ESC [C, ESC F$, ESC [C, O /# Keypad Right #/ + ESC [A, ESC [A, ESC [-1, O /# Keypad Up #/ + ESC [B, ESC [B, ESC [-2, O /# Keypad Down #/ + ESC [H, ESC [H, ESC [-263, O /# Keypad Home #/ + ESC [S, ESC [S, ESC [-19, O /# Keypad PgUp #/ + ESC [T, ESC [T, ESC [-20, O /# Keypad PgDn #/ + ESC [U, ESC [U, ESC [-21, O /# Keypad End #/ + nothing, nothing, nothing, O /# Kaypad 5 #/ + + Interix Console (term=interix, ncurses) + ============================== + KEY_LEFT, ESC F^, KEY_LEFT, KEY_LEFT /# Left #/ + KEY_RIGHT, ESC F$, KEY_RIGHT, KEY_RIGHT /# Right #/ + KEY_UP, 0x146, local win, KEY_UP /# Up #/ + KEY_DOWN, 0x145, local win, KEY_DOWN /# Down #/ + ESC [H, ESC [H, ESC [H, ESC [H /# Home #/ + KEY_PPAGE, local win, KEY_PPAGE, KEY_PPAGE /# Page Up #/ + KEY_NPAGE, local win, KEY_NPAGE, KEY_NPAGE /# Page Down #/ + KEY_LL, KEY_LL, KEY_LL, KEY_LL /# End #/ + KEY_LEFT, ESC F^, ESC [-4, O /# Keypad Left #/ + KEY_RIGHT, ESC F$, ESC [-3, O /# Keypad Right #/ + KEY_UP, KEY_UP, ESC [-1, O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, ESC [-2, O /# Keypad Down #/ + ESC [H, ESC [H, ESC [-263, O /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, ESC [-19, O /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, ESC [-20, O /# Keypad PgDn #/ + KEY_LL, KEY_LL, ESC [-21, O /# Keypad End #/ + nothing, nothing, nothing, O /# Keypad 5 #/ + + Cygwin Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC [D, ESC ESC [D /# Left #/ + ESC [C, ESC [C, ESC [C, ESC ESC [C /# Rght #/ + ESC [A, ESC [A, ESC [A, ESC ESC [A /# Up #/ + ESC [B, ESC [B, ESC [B, ESC ESC [B /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC ESC [1~ /# Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC ESC [5~ /# Page Up #/ + ESC [6~, ESC [6~, ESC [6~, ESC ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, ESC ESC [D,O /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, ESC ESC [C,O /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, ESC ESC [A,O /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, ESC ESC [B,O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC ESC [1~,O /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC ESC [5~,O /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, ESC ESC [6~,O /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, ESC ESC [4~,O /# Keypad End #/ + ESC [-71, nothing, nothing, O /# Keypad 5 #/ + + Cygwin Console (term=cygwin, ncurses) + ============================== + KEY_LEFT, KEY_LEFT, KEY_LEFT, ESC-260 /# Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, ESC-261 /# Rght #/ + KEY_UP, KEY_UP, KEY_UP, ESC-259 /# Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, ESC-258 /# Down #/ + KEY_HOME, KEY_HOME, KEY_HOME, ESC-262 /# Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, ESC-339 /# Page Up #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, ESC-338 /# Page Down #/ + KEY_END, KEY_END, KEY_END, ESC-360 /# End #/ + KEY_LEFT, KEY_LEFT, KEY_LEFT, ESC-260,O /# Keypad Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, ESC-261,O /# Keypad Right #/ + KEY_UP, KEY_UP, KEY_UP, ESC-259,O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, ESC-258,O /# Keypad Down #/ + KEY_HOME, KEY_HOME, KEY_HOME, ESC-262,O /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, ESC-339,O /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, ESC-338,O /# Keypad PgDn #/ + KEY_END, KEY_END, KEY_END, ESC-360,O /# Keypad End #/ + ESC [G, nothing, nothing, O /# Keypad 5 #/ + + MSYS Console (raw, ncurses) + ============================== + normal shift ctrl alt + ESC OD, ESC [d, ESC Od nothing /# Left #/ + ESC OE, ESC [e, ESC Oe, nothing /# Right #/ + ESC OA, ESC [a, ESC Oa, nothing /# Up #/ + ESC OB, ESC [b, ESC Ob, nothing /# Down #/ + ESC [7~, ESC [7$, ESC [7^, nothing /# Home #/ + ESC [5~, local window, ESC [5^, nothing /# Page Up #/ + ESC [6~, local window, ESC [6^, nothing /# Page Down #/ + ESC [8~, ESC [8$, ESC [8^, nothing /# End #/ + ESC OD, ESC [d, ESC Od O /# Keypad Left #/ + ESC OE, ESC [c, ESC Oc, O /# Keypad Right #/ + ESC OA, ESC [a, ESC Oa, O /# Keypad Up #/ + ESC OB, ESC [b, ESC Ob, O /# Keypad Down #/ + ESC [7~, ESC [7$, ESC [7^, O /# Keypad Home #/ + ESC [5~, local window, ESC [5^, O /# Keypad PgUp #/ + ESC [6~, local window, ESC [6^, O /# Keypad PgDn #/ + ESC [8~, ESC [8$, ESC [8^, O /# Keypad End #/ + 11, 11, 11, O /# Keypad 5 #/ + + MSYS Console (term=rxvt, ncurses) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_SLEFT, 514 nothing /# Left #/ + KEY_RIGHT, KEY_SRIGHT, 516, nothing /# Right #/ + KEY_UP, 518, 519, nothing /# Up #/ + KEY_DOWN, 511, 512, nothing /# Down #/ + KEY_HOME, KEY_SHOME, ESC [7^, nothing /# Home #/ + KEY_PPAGE, local window, ESC [5^, nothing /# Page Up #/ + KEY_NPAGE, local window, ESC [6^, nothing /# Page Down #/ + KEY_END, KEY_SEND, KEY_EOL, nothing /# End #/ + KEY_LEFT, KEY_SLEFT, 514 O /# Keypad Left #/ + KEY_RIGHT, KEY_SRIGHT, 516, O /# Keypad Right #/ + KEY_UP, 518, 519, O /# Keypad Up #/ + KEY_DOWN, 511, 512, O /# Keypad Down #/ + KEY_HOME, KEY_SHOME, ESC [7^, O /# Keypad Home #/ + KEY_PPAGE, local window, ESC [5^, O /# Keypad PgUp #/ + KEY_NPAGE, local window, ESC [6^, O /# Keypad PgDn #/ + KEY_END, KEY_SEND, KEY_EOL, O /# Keypad End #/ + 11, 11, 11, O /# Keypad 5 #/ + + Win32 Console (raw, pdcurses) + DJGPP Console (raw, pdcurses) + ============================== + normal shift ctrl alt + 260, 391, 443, 493 /# Left #/ + 261, 400, 444, 492 /# Right #/ + 259, 547, 480, 490 /# Up #/ + 258, 548, 481, 491 /# Down #/ + 262, 388, 447, 524 /# Home #/ + 339, 396, 445, 526 /# Page Up #/ + 338, 394, 446, 520 /# Page Down #/ + 358, 384, 448, 518 /# End #/ + 452, 52('4'), 511, 521 /# Keypad Left #/ + 454, 54('6'), 513, 523 /# Keypad Right #/ + 450, 56('8'), 515, 525 /# Keypad Up #/ + 456, 50('2'), 509, 519 /# Keypad Down #/ + 449, 55('7'), 514, 524 /# Keypad Home #/ + 451, 57('9'), 516, 526 /# Keypad PgUp #/ + 457, 51('3'), 510, 520 /# Keypad PgDn #/ + 455, 49('1'), 508, 518 /# Keypad End #/ + 453, 53('5'), 512, 522 /# Keypad 5 #/ + + Win32 Console (pdcurses, MSVC/MingW32) + DJGPP Console (pdcurses) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_SLEFT, CTL_LEFT, ALT_LEFT /# Left #/ + KEY_RIGHT, KEY_SRIGHT, CTL_RIGHT, ALT_RIGHT /# Right #/ + KEY_UP, KEY_SUP, CTL_UP, ALT_UP /# Up #/ + KEY_DOWN, KEY_SDOWN, CTL_DOWN, ALT_DOWN /# Down #/ + KEY_HOME, KEY_SHOME, CTL_HOME, ALT_HOME /# Home #/ + KEY_PPAGE, KEY_SPREVIOUS, CTL_PGUP, ALT_PGUP /# Page Up #/ + KEY_NPAGE, KEY_SNEXTE, CTL_PGDN, ALT_PGDN /# Page Down #/ + KEY_END, KEY_SEND, CTL_END, ALT_END /# End #/ + KEY_B1, 52('4'), CTL_PAD4, ALT_PAD4 /# Keypad Left #/ + KEY_B3, 54('6'), CTL_PAD6, ALT_PAD6 /# Keypad Right #/ + KEY_A2, 56('8'), CTL_PAD8, ALT_PAD8 /# Keypad Up #/ + KEY_C2, 50('2'), CTL_PAD2, ALT_PAD2 /# Keypad Down #/ + KEY_A1, 55('7'), CTL_PAD7, ALT_PAD7 /# Keypad Home #/ + KEY_A3, 57('9'), CTL_PAD9, ALT_PAD9 /# Keypad PgUp #/ + KEY_C3, 51('3'), CTL_PAD3, ALT_PAD3 /# Keypad PgDn #/ + KEY_C1, 49('1'), CTL_PAD1, ALT_PAD1 /# Keypad End #/ + KEY_B2, 53('5'), CTL_PAD5, ALT_PAD5 /# Keypad 5 #/ + + Windows Telnet (raw) + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC [D, ESC [D /# Left #/ + ESC [C, ESC [C, ESC [C, ESC [C /# Right #/ + ESC [A, ESC [A, ESC [A, ESC [A /# Up #/ + ESC [B, ESC [B, ESC [B, ESC [B /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC [5~ /# Page Up #/ + ESC [6~, ESC [6~, ESC [6~, ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, ESC [D /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, ESC [C /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, ESC [A /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, ESC [B /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, ESC [5~ /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, ESC [6~ /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# Keypad End #/ + nothing, nothing, nothing, nothing /# Keypad 5 #/ + + Windows Telnet (term=xterm) + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_LEFT, KEY_LEFT, KEY_LEFT /# Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, KEY_RIGHT /# Right #/ + KEY_UP, KEY_UP, KEY_UP, KEY_UP /# Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, KEY_DOWN /# Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, KEY_PPAGE /# Page Up #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, KEY_NPAGE /# Page Down #/ + ESC [4~, ESC [4~, ESC [4~, ESC [4~ /# End #/ + KEY_LEFT, KEY_LEFT, KEY_LEFT, O /# Keypad Left #/ + KEY_RIGHT, KEY_RIGHT, KEY_RIGHT, O /# Keypad Right #/ + KEY_UP, KEY_UP, KEY_UP, O /# Keypad Up #/ + KEY_DOWN, KEY_DOWN, KEY_DOWN, O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, ESC [1~ /# Keypad Home #/ + KEY_PPAGE, KEY_PPAGE, KEY_PPAGE, KEY_PPAGE /# Keypad PgUp #/ + KEY_NPAGE, KEY_NPAGE, KEY_NPAGE, KEY_NPAGE /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, O /# Keypad End #/ + ESC [-71, nothing, nothing, O /# Keypad 5 #/ + + PuTTY + ============================== + normal shift ctrl alt + ESC [D, ESC [D, ESC OD, ESC [D /# Left #/ + ESC [C, ESC [C, ESC OC, ESC [C /# Right #/ + ESC [A, ESC [A, ESC OA, ESC [A /# Up #/ + ESC [B, ESC [B, ESC OB, ESC [B /# Down #/ + ESC [1~, ESC [1~, local win, ESC [1~ /# Home #/ + ESC [5~, local win, local win, ESC [5~ /# Page Up #/ + ESC [6~, local win, local win, ESC [6~ /# Page Down #/ + ESC [4~, ESC [4~, local win, ESC [4~ /# End #/ + ESC [D, ESC [D, ESC [D, O /# Keypad Left #/ + ESC [C, ESC [C, ESC [C, O /# Keypad Right #/ + ESC [A, ESC [A, ESC [A, O /# Keypad Up #/ + ESC [B, ESC [B, ESC [B, O /# Keypad Down #/ + ESC [1~, ESC [1~, ESC [1~, O /# Keypad Home #/ + ESC [5~, ESC [5~, ESC [5~, O /# Keypad PgUp #/ + ESC [6~, ESC [6~, ESC [6~, O /# Keypad PgDn #/ + ESC [4~, ESC [4~, ESC [4~, O /# Keypad End #/ + nothing, nothing, nothing, O /# Keypad 5 #/ + + PuTTY + ============================== + normal shift ctrl alt + KEY_LEFT, KEY_LEFT, ESC OD, ESC KEY_LEFT /# Left #/ + KEY_RIGHT KEY_RIGHT, ESC OC, ESC KEY_RIGHT /# Right #/ + KEY_UP, KEY_UP, ESC OA, ESC KEY_UP /# Up #/ + KEY_DOWN, KEY_DOWN, ESC OB, ESC KEY_DOWN /# Down #/ + ESC [1~, ESC [1~, local win, ESC ESC [1~ /# Home #/ + KEY_PPAGE local win, local win, ESC KEY_PPAGE /# Page Up #/ + KEY_NPAGE local win, local win, ESC KEY_NPAGE /# Page Down #/ + ESC [4~, ESC [4~, local win, ESC ESC [4~ /# End #/ + ESC Ot, ESC Ot, ESC Ot, O /# Keypad Left #/ + ESC Ov, ESC Ov, ESC Ov, O /# Keypad Right #/ + ESC Ox, ESC Ox, ESC Ox, O /# Keypad Up #/ + ESC Or, ESC Or, ESC Or, O /# Keypad Down #/ + ESC Ow, ESC Ow, ESC Ow, O /# Keypad Home #/ + ESC Oy, ESC Oy, ESC Oy, O /# Keypad PgUp #/ + ESC Os, ESC Os, ESC Os, O /# Keypad PgDn #/ + ESC Oq, ESC Oq, ESC Oq, O /# Keypad End #/ + ESC Ou, ESC Ou, ESC Ou, O /# Keypad 5 #/ +*/ + +#define M_NORMAL 0 +#define M_ESC 1 +#define M_KEYPAD 2 +#define M_TRAIL 3 + +#ifndef BUILD_ROGUE +int +md_readchar() +{ + int ch = 0; + int lastch = 0; + int mode = M_NORMAL; + int mode2 = M_NORMAL; + + for(;;) + { + ch = getch(); + + if (ch == ERR) /* timed out waiting for valid sequence */ + { /* flush input so far and start over */ + mode = M_NORMAL; + nocbreak(); + raw(); + ch = 27; + break; + } + + if (mode == M_TRAIL) + { + if (ch == '^') /* msys console : 7,5,6,8: modified*/ + ch = CTRL( toupper(lastch) ); + + if (ch == '~') /* cygwin console: 1,5,6,4: normal */ + ch = tolower(lastch); /* windows telnet: 1,5,6,4: normal */ + /* msys console : 7,5,6,8: normal */ + + if (mode2 == M_ESC) /* cygwin console: 1,5,6,4: modified*/ + ch = CTRL( toupper(ch) ); + + break; + } + + if (mode == M_ESC) + { + if (ch == 27) + { + mode2 = M_ESC; + continue; + } + + if ((ch == 'F') || (ch == 'O') || (ch == '[')) + { + mode = M_KEYPAD; + continue; + } + + + switch(ch) + { + /* Cygwin Console */ + /* PuTTY */ + case KEY_LEFT : ch = CTRL('H'); break; + case KEY_RIGHT: ch = CTRL('L'); break; + case KEY_UP : ch = CTRL('K'); break; + case KEY_DOWN : ch = CTRL('J'); break; + case KEY_HOME : ch = CTRL('Y'); break; + case KEY_PPAGE: ch = CTRL('U'); break; + case KEY_NPAGE: ch = CTRL('N'); break; + case KEY_END : ch = CTRL('B'); break; + + default: break; + } + + break; + } + + if (mode == M_KEYPAD) + { + switch(ch) + { + /* ESC F - Interix Console codes */ + case '^': ch = CTRL('H'); break; /* Shift-Left */ + case '$': ch = CTRL('L'); break; /* Shift-Right */ + + /* ESC [ - Interix Console codes */ + case 'H': ch = 'y'; break; /* Home */ + case 1: ch = CTRL('K'); break; /* Ctl-Keypad Up */ + case 2: ch = CTRL('J'); break; /* Ctl-Keypad Down */ + case 3: ch = CTRL('L'); break; /* Ctl-Keypad Right */ + case 4: ch = CTRL('H'); break; /* Ctl-Keypad Left */ + case 263: ch = CTRL('Y'); break; /* Ctl-Keypad Home */ + case 19: ch = CTRL('U'); break; /* Ctl-Keypad PgUp */ + case 20: ch = CTRL('N'); break; /* Ctl-Keypad PgDn */ + case 21: ch = CTRL('B'); break; /* Ctl-Keypad End */ + + /* ESC [ - Cygwin Console codes */ + case 'G': ch = '.'; break; /* Keypad 5 */ + case '7': lastch = 'Y'; mode=M_TRAIL; break; /* Ctl-Home */ + case '5': lastch = 'U'; mode=M_TRAIL; break; /* Ctl-PgUp */ + case '6': lastch = 'N'; mode=M_TRAIL; break; /* Ctl-PgDn */ + + /* ESC [ - Win32 Telnet, PuTTY */ + case '1': lastch = 'y'; mode=M_TRAIL; break; /* Home */ + case '4': lastch = 'b'; mode=M_TRAIL; break; /* End */ + + /* ESC O - PuTTY */ + case 'D': ch = CTRL('H'); break; + case 'C': ch = CTRL('L'); break; + case 'A': ch = CTRL('K'); break; + case 'B': ch = CTRL('J'); break; + case 't': ch = 'h'; break; + case 'v': ch = 'l'; break; + case 'x': ch = 'k'; break; + case 'r': ch = 'j'; break; + case 'w': ch = 'y'; break; + case 'y': ch = 'u'; break; + case 's': ch = 'n'; break; + case 'q': ch = 'b'; break; + case 'u': ch = '.'; break; + } + + if (mode != M_KEYPAD) + continue; + } + + if (ch == 27) + { + halfdelay(1); + mode = M_ESC; + continue; + } + + switch(ch) + { + case KEY_LEFT : ch = 'h'; break; + case KEY_DOWN : ch = 'j'; break; + case KEY_UP : ch = 'k'; break; + case KEY_RIGHT : ch = 'l'; break; + case KEY_HOME : ch = 'y'; break; + case KEY_PPAGE : ch = 'u'; break; + case KEY_END : ch = 'b'; break; +#ifdef KEY_LL + case KEY_LL : ch = 'b'; break; +#endif + case KEY_NPAGE : ch = 'n'; break; + +#ifdef KEY_B1 + case KEY_B1 : ch = 'h'; break; + case KEY_C2 : ch = 'j'; break; + case KEY_A2 : ch = 'k'; break; + case KEY_B3 : ch = 'l'; break; +#endif + case KEY_A1 : ch = 'y'; break; + case KEY_A3 : ch = 'u'; break; + case KEY_C1 : ch = 'b'; break; + case KEY_C3 : ch = 'n'; break; + /* next should be '.', but for problem with putty/linux */ + case KEY_B2 : ch = 'u'; break; + +#ifdef KEY_SLEFT + case KEY_SRIGHT : ch = CTRL('L'); break; + case KEY_SLEFT : ch = CTRL('H'); break; +#ifdef KEY_SUP + case KEY_SUP : ch = CTRL('K'); break; + case KEY_SDOWN : ch = CTRL('J'); break; +#endif + case KEY_SHOME : ch = CTRL('Y'); break; + case KEY_SPREVIOUS:ch = CTRL('U'); break; + case KEY_SEND : ch = CTRL('B'); break; + case KEY_SNEXT : ch = CTRL('N'); break; +#endif + case 0x146 : ch = CTRL('K'); break; /* Shift-Up */ + case 0x145 : ch = CTRL('J'); break; /* Shift-Down */ + + +#ifdef CTL_RIGHT + case CTL_RIGHT : ch = CTRL('L'); break; + case CTL_LEFT : ch = CTRL('H'); break; + case CTL_UP : ch = CTRL('K'); break; + case CTL_DOWN : ch = CTRL('J'); break; + case CTL_HOME : ch = CTRL('Y'); break; + case CTL_PGUP : ch = CTRL('U'); break; + case CTL_END : ch = CTRL('B'); break; + case CTL_PGDN : ch = CTRL('N'); break; +#endif +#ifdef KEY_EOL + case KEY_EOL : ch = CTRL('B'); break; +#endif + +#ifndef CTL_PAD1 + /* MSYS rxvt console */ + case 511 : ch = CTRL('J'); break; /* Shift Dn */ + case 512 : ch = CTRL('J'); break; /* Ctl Down */ + case 514 : ch = CTRL('H'); break; /* Ctl Left */ + case 516 : ch = CTRL('L'); break; /* Ctl Right*/ + case 518 : ch = CTRL('K'); break; /* Shift Up */ + case 519 : ch = CTRL('K'); break; /* Ctl Up */ +#endif + +#ifdef CTL_PAD1 + case CTL_PAD1 : ch = CTRL('B'); break; + case CTL_PAD2 : ch = CTRL('J'); break; + case CTL_PAD3 : ch = CTRL('N'); break; + case CTL_PAD4 : ch = CTRL('H'); break; + case CTL_PAD5 : ch = '.'; break; + case CTL_PAD6 : ch = CTRL('L'); break; + case CTL_PAD7 : ch = CTRL('Y'); break; + case CTL_PAD8 : ch = CTRL('K'); break; + case CTL_PAD9 : ch = CTRL('U'); break; +#endif + +#ifdef ALT_RIGHT + case ALT_RIGHT : ch = CTRL('L'); break; + case ALT_LEFT : ch = CTRL('H'); break; + case ALT_DOWN : ch = CTRL('J'); break; + case ALT_HOME : ch = CTRL('Y'); break; + case ALT_PGUP : ch = CTRL('U'); break; + case ALT_END : ch = CTRL('B'); break; + case ALT_PGDN : ch = CTRL('N'); break; +#endif + +#ifdef ALT_PAD1 + case ALT_PAD1 : ch = CTRL('B'); break; + case ALT_PAD2 : ch = CTRL('J'); break; + case ALT_PAD3 : ch = CTRL('N'); break; + case ALT_PAD4 : ch = CTRL('H'); break; + case ALT_PAD5 : ch = '.'; break; + case ALT_PAD6 : ch = CTRL('L'); break; + case ALT_PAD7 : ch = CTRL('Y'); break; + case ALT_PAD8 : ch = CTRL('K'); break; + case ALT_PAD9 : ch = CTRL('U'); break; +#endif +#ifdef KEY_BACKSPACE /* NCURSES in Keypad mode sends this for Ctrl-H */ + case KEY_BACKSPACE: ch = CTRL('H'); break; +#endif + } + + break; + } + + nocbreak(); /* disable halfdelay mode if on */ + raw(); + + return(ch & 0x7F); +} +#endif + +#if defined(LOADAV) && defined(HAVE_NLIST_H) && defined(HAVE_NLIST) +/* + * loadav: + * Looking up load average in core (for system where the loadav() + * system call isn't defined + */ + +#include + +struct nlist avenrun = { + "_avenrun" +}; + +void +md_loadav(double *avg) +{ + int kmem; + + if ((kmem = open("/dev/kmem", 0)) < 0) + goto bad; + nlist(NAMELIST, &avenrun); + if (avenrun.n_type == 0) + { + close(kmem); +bad: + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + return; + } + + lseek(kmem, avenrun.n_value, 0); + read(kmem, (char *) avg, 3 * sizeof (double)); + close(kmem); +} +#else +void +md_loadav(double *avg) +{ +#if defined(HAVE_LOADAV) + loadav(avg); +#elif defined(HAVE_GETLOADAVG) + getloadavg(avg,3); +#else + avg[0] = avg[1] = avg[2] = 0; +#endif +} +#endif + +#ifndef NSIG +#define NSIG 32 +#endif + +void +md_ignoreallsignals() +{ + int i; + + for (i = 0; i < NSIG; i++) + signal(i, SIG_IGN); +} + +void +md_tstphold() +{ +#ifdef SIGTSTP + /* + * If a process can be suspended, this code wouldn't work + */ +# ifdef SIG_HOLD + signal(SIGTSTP, SIG_HOLD); +# else + signal(SIGTSTP, SIG_IGN); +# endif +#endif +} + +void +md_tstpresume() +{ +#ifdef SIGTSTP + signal(SIGTSTP, tstp); +#endif +} + +void +md_tstpsignal() +{ +#ifdef SIGTSTP + kill(0, SIGTSTP); /* send actual signal and suspend process */ +#endif +} + +#if defined(CHECKTIME) +void +md_start_checkout_timer(int time) +{ + int checkout(); + +#if defined(HAVE_ALARM) && defined(SIGALRM) + signal(SIGALRM, checkout); + alarm(time); +#endif +} + +void +md_stop_checkout_timer() +{ +#if defined(SIGALRM) + signal(SIGALRM, SIG_IGN); +#endif +} + +#endif diff --git a/src/cc/rogue/misc.c b/src/cc/rogue/misc.c new file mode 100644 index 000000000..5f9a89dcc --- /dev/null +++ b/src/cc/rogue/misc.c @@ -0,0 +1,617 @@ +/* + * All sorts of miscellaneous routines + * + * @(#)misc.c 4.66 (Berkeley) 08/06/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +//#include +#include "rogue.h" + +/* + * look: + * A quick glance all around the player + */ +#undef DEBUG + + +void +look(struct rogue_state *rs,bool wakeup) +{ + int x, y; + int ch; + THING *tp; + PLACE *pp; + struct room *rp; + int ey, ex; + int passcount; + char pfl, *fp, pch; + int sy, sx, sumhero = 0, diffhero = 0; +# ifdef DEBUG + static bool done = FALSE; + + if (done) + return; + done = TRUE; +# endif /* DEBUG */ + passcount = 0; + rp = proom; + if (!ce(oldpos, hero)) + { + erase_lamp(&oldpos, oldrp); + oldpos = hero; + oldrp = rp; + } + ey = hero.y + 1; + ex = hero.x + 1; + sx = hero.x - 1; + sy = hero.y - 1; + if (door_stop && !firstmove && running) + { + sumhero = hero.y + hero.x; + diffhero = hero.y - hero.x; + } + pp = INDEX(hero.y, hero.x); + pch = pp->p_ch; + pfl = pp->p_flags; + + for (y = sy; y <= ey; y++) + if (y > 0 && y < NUMLINES - 1) for (x = sx; x <= ex; x++) + { + if (x < 0 || x >= NUMCOLS) + continue; + if (!on(player, ISBLIND)) + { + if (y == hero.y && x == hero.x) + continue; + } + + pp = INDEX(y, x); + ch = pp->p_ch; + if (ch == ' ') /* nothing need be done with a ' ' */ + continue; + fp = &pp->p_flags; + if (pch != DOOR && ch != DOOR) + if ((pfl & F_PASS) != (*fp & F_PASS)) + continue; + if (((*fp & F_PASS) || ch == DOOR) && + ((pfl & F_PASS) || pch == DOOR)) + { + if (hero.x != x && hero.y != y && + !step_ok(chat(y, hero.x)) && !step_ok(chat(hero.y, x))) + continue; + } + + if ((tp = pp->p_monst) == NULL) + ch = trip_ch(y, x, ch); + else + if (on(player, SEEMONST) && on(*tp, ISINVIS)) + { + if (door_stop && !firstmove) + running = FALSE; + continue; + } + else + { + if (wakeup) + wake_monster(rs,y, x); + if (see_monst(tp)) + { + if (on(player, ISHALU)) + ch = rnd(26) + 'A'; + else + ch = tp->t_disguise; + } + } + if (on(player, ISBLIND) && (y != hero.y || x != hero.x)) + continue; + + move(y, x); + + if ((proom->r_flags & ISDARK) && !see_floor && ch == FLOOR) + ch = ' '; + + if (tp != NULL || ch != CCHAR( inch() )) + addch(ch); + + if (door_stop && !firstmove && running) + { + switch (runch) + { + case 'h': + if (x == ex) + continue; + when 'j': + if (y == sy) + continue; + when 'k': + if (y == ey) + continue; + when 'l': + if (x == sx) + continue; + when 'y': + if ((y + x) - sumhero >= 1) + continue; + when 'u': + if ((y - x) - diffhero >= 1) + continue; + when 'n': + if ((y + x) - sumhero <= -1) + continue; + when 'b': + if ((y - x) - diffhero <= -1) + continue; + } + switch (ch) + { + case DOOR: + if (x == hero.x || y == hero.y) + running = FALSE; + break; + case PASSAGE: + if (x == hero.x || y == hero.y) + passcount++; + break; + case FLOOR: + case '|': + case '-': + case ' ': + break; + default: + running = FALSE; + break; + } + } + } + if (door_stop && !firstmove && passcount > 1) + running = FALSE; + if (!running || !jump) + mvaddch(hero.y, hero.x, PLAYER); +# ifdef DEBUG + done = FALSE; +# endif /* DEBUG */ +} + +/* + * trip_ch: + * Return the character appropriate for this space, taking into + * account whether or not the player is tripping. + */ +int +trip_ch(int y, int x, int ch) +{ + if (on(player, ISHALU) && after) + switch (ch) + { + case FLOOR: + case ' ': + case PASSAGE: + case '-': + case '|': + case DOOR: + case TRAP: + break; + default: + if (y != stairs.y || x != stairs.x || !seenstairs) + ch = rnd_thing(); + break; + } + return ch; +} + +/* + * erase_lamp: + * Erase the area shown by a lamp in a dark room. + */ + +void +erase_lamp(coord *pos, struct room *rp) +{ + int y, x, ey, sy, ex; + + if (!(see_floor && (rp->r_flags & (ISGONE|ISDARK)) == ISDARK + && !on(player,ISBLIND))) + return; + + ey = pos->y + 1; + ex = pos->x + 1; + sy = pos->y - 1; + for (x = pos->x - 1; x <= ex; x++) + for (y = sy; y <= ey; y++) + { + if (y == hero.y && x == hero.x) + continue; + move(y, x); + if (inch() == FLOOR) + addch(' '); + } +} + +/* + * show_floor: + * Should we show the floor in her room at this time? + */ +bool +show_floor() +{ + if ((proom->r_flags & (ISGONE|ISDARK)) == ISDARK && !on(player, ISBLIND)) + return see_floor; + else + return TRUE; +} + +/* + * find_obj: + * Find the unclaimed object at y, x + */ +THING * +find_obj(struct rogue_state *rs,int y, int x) +{ + THING *obj; + + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + { + if (obj->o_pos.y == y && obj->o_pos.x == x) + return obj; + } +#ifdef MASTER + sprintf(prbuf, "Non-object %d,%d", y, x); + msg(rs,prbuf); + return NULL; +#else + /* NOTREACHED */ + return NULL; +#endif +} + +/* + * eat: + * She wants to eat something, so let her try + */ + +void +eat(struct rogue_state *rs) +{ + THING *obj; + + if ((obj = get_item(rs,"eat", FOOD)) == NULL) + return; + if (obj->o_type != FOOD) + { + if (!terse) + msg(rs,"ugh, you would get ill if you ate that"); + else + msg(rs,"that's Inedible!"); + return; + } + if (food_left < 0) + food_left = 0; + if ((food_left += HUNGERTIME - 200 + rnd(400)) > STOMACHSIZE) + food_left = STOMACHSIZE; + hungry_state = 0; + if (obj == cur_weapon) + cur_weapon = NULL; + if (obj->o_which == 1) + msg(rs,"my, that was a yummy %s", fruit); + else + if (rnd(100) > 70) + { + pstats.s_exp++; + msg(rs,"%s, this food tastes awful", choose_str("bummer", "yuk")); + check_level(rs); + } + else + msg(rs,"%s, that tasted good", choose_str("oh, wow", "yum")); + leave_pack(rs,obj, FALSE, FALSE); +} + +/* + * check_level: + * Check to see if the guy has gone up a level. + */ + +void +check_level(struct rogue_state *rs) +{ + int i, add, olevel; + + for (i = 0; e_levels[i] != 0; i++) + if (e_levels[i] > pstats.s_exp) + break; + i++; + olevel = pstats.s_lvl; + pstats.s_lvl = i; + if (i > olevel) + { + add = roll(i - olevel, 10); + max_hp += add; + pstats.s_hpt += add; + msg(rs,"welcome to level %d", i); + } +} + +/* + * chg_str: + * used to modify the playes strength. It keeps track of the + * highest it has been, just in case + */ + +void +chg_str(int amt) +{ + //auto jl777: strange compiler error + uint32_t comp; + + if (amt == 0) + return; + //add_str(&pstats.s_str, amt); + pstats.s_str += amt; + if ( pstats.s_str < 3 ) + pstats.s_str = 3; + else if ( pstats.s_str > 31 ) + pstats.s_str = 31; + comp = pstats.s_str; + if (ISRING(LEFT, R_ADDSTR)) + { + // add_str(&comp, -cur_ring[LEFT]->o_arm); + comp += -cur_ring[LEFT]->o_arm; + if ( comp < 3 ) + comp = 3; + else if ( comp > 31 ) + comp = 31; + } + if (ISRING(RIGHT, R_ADDSTR)) + { + //add_str(&comp, -cur_ring[RIGHT]->o_arm); + comp += -cur_ring[RIGHT]->o_arm; + if ( comp < 3 ) + comp = 3; + else if ( comp > 31 ) + comp = 31; + } + if ( comp > max_stats.s_str ) + max_stats.s_str = comp; +} + +/* + * add_str: + * Perform the actual add, checking upper and lower bound limits + */ +void +add_str(str_t *sp, int amt) +{ + if ((*sp += amt) < 3) + *sp = 3; + else if (*sp > 31) + *sp = 31; +} + +/* + * add_haste: + * Add a haste to the player + */ +bool +add_haste(struct rogue_state *rs,bool potion) +{ + if (on(player, ISHASTE)) + { + no_command += rnd(8); + player.t_flags &= ~(ISRUN|ISHASTE); + extinguish(nohaste); + msg(rs,"you faint from exhaustion"); + return FALSE; + } + else + { + player.t_flags |= ISHASTE; + if (potion) + fuse(nohaste, 0, rnd(4)+4, AFTER); + return TRUE; + } +} + +/* + * aggravate: + * Aggravate all the monsters on this level + */ + +void +aggravate(struct rogue_state *rs) +{ + THING *mp; + + for (mp = mlist; mp != NULL; mp = next(mp)) + runto(rs,&mp->t_pos); +} + +/* + * vowelstr: + * For printfs: if string starts with a vowel, return "n" for an + * "an". + */ +char * +vowelstr(char *str) +{ + switch (*str) + { + case 'a': case 'A': + case 'e': case 'E': + case 'i': case 'I': + case 'o': case 'O': + case 'u': case 'U': + return "n"; + default: + return ""; + } +} + +/* + * is_current: + * See if the object is one of the currently used items + */ +bool +is_current(struct rogue_state *rs,THING *obj) +{ + if (obj == NULL) + return FALSE; + if (obj == cur_armor || obj == cur_weapon || obj == cur_ring[LEFT] + || obj == cur_ring[RIGHT]) + { + if (!terse) + addmsg(rs,"That's already "); + msg(rs,"in use"); + return TRUE; + } + return FALSE; +} + +/* + * get_dir: + * Set up the direction co_ordinate for use in varios "prefix" + * commands + */ +bool +get_dir(struct rogue_state *rs) +{ + char *prompt; + bool gotit; + static coord last_delt= {0,0}; + + if (again && last_dir != '\0') + { + delta.y = last_delt.y; + delta.x = last_delt.x; + dir_ch = last_dir; + } + else + { + if (!terse) + msg(rs,prompt = "which direction? "); + else + prompt = "direction: "; + do + { + gotit = TRUE; + switch (dir_ch = readchar(rs)) + { + case 'h': case'H': delta.y = 0; delta.x = -1; + when 'j': case'J': delta.y = 1; delta.x = 0; + when 'k': case'K': delta.y = -1; delta.x = 0; + when 'l': case'L': delta.y = 0; delta.x = 1; + when 'y': case'Y': delta.y = -1; delta.x = -1; + when 'u': case'U': delta.y = -1; delta.x = 1; + when 'b': case'B': delta.y = 1; delta.x = -1; + when 'n': case'N': delta.y = 1; delta.x = 1; + when ESCAPE: last_dir = '\0'; reset_last(); return FALSE; + otherwise: + mpos = 0; + msg(rs,prompt); + gotit = FALSE; + } + } until (gotit); + if (isupper(dir_ch)) + dir_ch = (char) tolower(dir_ch); + last_dir = dir_ch; + last_delt.y = delta.y; + last_delt.x = delta.x; + } + if (on(player, ISHUH) && rnd(5) == 0) + do + { + delta.y = rnd(3) - 1; + delta.x = rnd(3) - 1; + } while (delta.y == 0 && delta.x == 0); + mpos = 0; + return TRUE; +} + +/* + * sign: + * Return the sign of the number + */ +int +sign(int nm) +{ + if (nm < 0) + return -1; + else + return (nm > 0); +} + +/* + * spread: + * Give a spread around a given number (+/- 20%) + */ +int +spread(int nm) +{ + return nm - nm / 20 + rnd(nm / 10); +} + +/* + * call_it: + * Call an object something after use. + */ + +void +call_it(struct rogue_state *rs,struct obj_info *info) +{ + if (info->oi_know) + { + if (info->oi_guess) + { + free(info->oi_guess); + info->oi_guess = NULL; + } + } + else if (!info->oi_guess) + { + msg(rs,terse ? (char *)"call it: " : (char *)"what do you want to call it? "); + if (get_str(rs,prbuf, stdscr) == NORM) + { + if (info->oi_guess != NULL) + free(info->oi_guess); + info->oi_guess = (char *)malloc((unsigned int) strlen(prbuf) + 1); + strcpy(info->oi_guess, prbuf); + } + } +} + +/* + * rnd_thing: + * Pick a random thing appropriate for this level + */ +char +rnd_thing() +{ + int i; + static char thing_list[] = { + POTION, SCROLL, RING, STICK, FOOD, WEAPON, ARMOR, STAIRS, GOLD, AMULET + }; + + if (level >= AMULETLEVEL) + i = rnd(sizeof thing_list / sizeof (char)); + else + i = rnd(sizeof thing_list / sizeof (char) - 1); + return thing_list[i]; +} + +/* + str str: + * Choose the first or second string depending on whether it the + * player is tripping + */ +char * +choose_str(char *ts, char *ns) +{ + return (on(player, ISHALU) ? ts : ns); +} diff --git a/src/cc/rogue/monsters.c b/src/cc/rogue/monsters.c new file mode 100644 index 000000000..7e1186e8d --- /dev/null +++ b/src/cc/rogue/monsters.c @@ -0,0 +1,255 @@ +/* + * File with various monster functions in it + * + * @(#)monsters.c 4.46 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" +//#include + +/* + * List of monsters in rough order of vorpalness + */ +static char lvl_mons[] = { + 'K', 'E', 'B', 'S', 'H', 'I', 'R', 'O', 'Z', 'L', 'C', 'Q', 'A', + 'N', 'Y', 'F', 'T', 'W', 'P', 'X', 'U', 'M', 'V', 'G', 'J', 'D' +}; + +static char wand_mons[] = { + 'K', 'E', 'B', 'S', 'H', 0, 'R', 'O', 'Z', 0, 'C', 'Q', 'A', + 0, 'Y', 0, 'T', 'W', 'P', 0, 'U', 'M', 'V', 'G', 'J', 0 +}; + +/* + * randmonster: + * Pick a monster to show up. The lower the level, + * the meaner the monster. + */ +char +randmonster(bool wander) +{ + int d; + char *mons; + + mons = (wander ? wand_mons : lvl_mons); + do + { + d = level + (rnd(10) - 6); + if (d < 0) + d = rnd(5); + if (d > 25) + d = rnd(5) + 21; + } while (mons[d] == 0); + return mons[d]; +} + +/* + * new_monster: + * Pick a new monster and add it to the list + */ + +void +new_monster(struct rogue_state *rs,THING *tp, char type, coord *cp) +{ + struct monster *mp; + int lev_add; + + if ((lev_add = level - AMULETLEVEL) < 0) + lev_add = 0; + attach(mlist, tp); + tp->t_type = type; + tp->t_disguise = type; + tp->t_pos = *cp; + move(cp->y, cp->x); + tp->t_oldch = CCHAR( inch() ); + tp->t_room = roomin(rs,cp); + moat(cp->y, cp->x) = tp; + mp = &monsters[tp->t_type-'A']; + tp->t_stats.s_lvl = mp->m_stats.s_lvl + lev_add; + tp->t_stats.s_maxhp = tp->t_stats.s_hpt = roll(tp->t_stats.s_lvl, 8); + tp->t_stats.s_arm = mp->m_stats.s_arm - lev_add; + strcpy(tp->t_stats.s_dmg,mp->m_stats.s_dmg); + tp->t_stats.s_str = mp->m_stats.s_str; + tp->t_stats.s_exp = mp->m_stats.s_exp + lev_add * 10 + exp_add(tp); + tp->t_flags = mp->m_flags; + if (level > 29) + tp->t_flags |= ISHASTE; + tp->t_turn = TRUE; + tp->t_pack = NULL; + if (ISWEARING(R_AGGR)) + runto(rs,cp); + if (type == 'X') + tp->t_disguise = rnd_thing(); +} + +/* + * expadd: + * Experience to add for this monster's level/hit points + */ +int +exp_add(THING *tp) +{ + int mod; + + if (tp->t_stats.s_lvl == 1) + mod = tp->t_stats.s_maxhp / 8; + else + mod = tp->t_stats.s_maxhp / 6; + if (tp->t_stats.s_lvl > 9) + mod *= 20; + else if (tp->t_stats.s_lvl > 6) + mod *= 4; + return mod; +} + +/* + * wanderer: + * Create a new wandering monster and aim it at the player + */ + +void +wanderer(struct rogue_state *rs) +{ + THING *tp; + static coord cp; + + tp = new_item(); + do + { + find_floor(rs,(struct room *) NULL, &cp, FALSE, TRUE); + } while (roomin(rs,&cp) == proom); + new_monster(rs,tp, randmonster(TRUE), &cp); + if (on(player, SEEMONST)) + { + standout(); + if (!on(player, ISHALU)) + addch(tp->t_type); + else + addch(rnd(26) + 'A'); + standend(); + } + runto(rs,&tp->t_pos); +#ifdef MASTER + if (wizard) + msg(rs,"started a wandering %s", monsters[tp->t_type-'A'].m_name); +#endif +} + +/* + * wake_monster: + * What to do when the hero steps next to a monster + */ +THING * +wake_monster(struct rogue_state *rs,int y, int x) +{ + THING *tp; + struct room *rp; + char ch, *mname; + +#ifdef MASTER + if ((tp = moat(y, x)) == NULL) + msg(rs,"can't find monster in wake_monster"); +#else + tp = moat(y, x); + if (tp == NULL) + endwin(), abort(); +#endif + ch = tp->t_type; + /* + * Every time he sees mean monster, it might start chasing him + */ + if (!on(*tp, ISRUN) && rnd(3) != 0 && on(*tp, ISMEAN) && !on(*tp, ISHELD) + && !ISWEARING(R_STEALTH) && !on(player, ISLEVIT)) + { + tp->t_dest = &hero; + tp->t_flags |= ISRUN; + } + if (ch == 'M' && !on(player, ISBLIND) && !on(player, ISHALU) + && !on(*tp, ISFOUND) && !on(*tp, ISCANC) && on(*tp, ISRUN)) + { + rp = proom; + if ((rp != NULL && !(rp->r_flags & ISDARK)) + || dist(y, x, hero.y, hero.x) < LAMPDIST) + { + tp->t_flags |= ISFOUND; + if (!save(VS_MAGIC)) + { + if (on(player, ISHUH)) + lengthen(unconfuse, spread(HUHDURATION)); + else + fuse(unconfuse, 0, spread(HUHDURATION), AFTER); + player.t_flags |= ISHUH; + mname = set_mname(tp); + addmsg(rs,"%s", mname); + if (strcmp(mname, "it") != 0) + addmsg(rs,"'"); + msg(rs,"s gaze has confused you"); + } + } + } + /* + * Let greedy ones guard gold + */ + if (on(*tp, ISGREED) && !on(*tp, ISRUN)) + { + tp->t_flags |= ISRUN; + if (proom->r_goldval) + tp->t_dest = &proom->r_gold; + else + tp->t_dest = &hero; + } + return tp; +} + +/* + * give_pack: + * Give a pack to a monster if it deserves one + */ + +void +give_pack(struct rogue_state *rs,THING *tp) +{ + if (level >= max_level && rnd(100) < monsters[tp->t_type-'A'].m_carry) + { + //fprintf(stderr,"give_pack\n"); + attach(tp->t_pack, new_thing(rs)); + } +} + +/* + * save_throw: + * See if a creature save against something + */ +int +save_throw(int which, THING *tp) +{ + int need; + + need = 14 + which - tp->t_stats.s_lvl / 2; + return (roll(1, 20) >= need); +} + +/* + * save: + * See if he saves against various nasty things + */ +int +save(int which) +{ + if (which == VS_MAGIC) + { + if (ISRING(LEFT, R_PROTECT)) + which -= cur_ring[LEFT]->o_arm; + if (ISRING(RIGHT, R_PROTECT)) + which -= cur_ring[RIGHT]->o_arm; + } + return save_throw(which, &player); +} diff --git a/src/cc/rogue/move.c b/src/cc/rogue/move.c new file mode 100644 index 000000000..c70d668ca --- /dev/null +++ b/src/cc/rogue/move.c @@ -0,0 +1,426 @@ +/* + * hero movement commands + * + * @(#)move.c 4.49 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" + +/* + * used to hold the new hero position + */ + +coord nh; + +/* + * do_run: + * Start the hero running + */ + +void +do_run(char ch) +{ + running = TRUE; + after = FALSE; + runch = ch; +} + +/* + * do_move: + * Check to see that a move is legal. If it is handle the + * consequences (fighting, picking up, etc.) + */ + +void +do_move(struct rogue_state *rs,int dy, int dx) +{ + char ch, fl; + + firstmove = FALSE; + if (no_move) + { + no_move--; + msg(rs,"you are still stuck in the bear trap"); + return; + } + /* + * Do a confused move (maybe) + */ + if (on(player, ISHUH) && rnd(5) != 0) + { + nh = *rndmove(&player); + if (ce(nh, hero)) + { + after = FALSE; + running = FALSE; + to_death = FALSE; + return; + } + } + else + { +over: + nh.y = hero.y + dy; + nh.x = hero.x + dx; + } + + /* + * Check if he tried to move off the screen or make an illegal + * diagonal move, and stop him if he did. + */ + if (nh.x < 0 || nh.x >= NUMCOLS || nh.y <= 0 || nh.y >= NUMLINES - 1) + goto hit_bound; + if (!diag_ok(&hero, &nh)) + { + after = FALSE; + running = FALSE; + return; + } + if (running && ce(hero, nh)) + after = running = FALSE; + fl = flat(nh.y, nh.x); + ch = winat(nh.y, nh.x); + if (!(fl & F_REAL) && ch == FLOOR) + { + if (!on(player, ISLEVIT)) + { + chat(nh.y, nh.x) = ch = TRAP; + flat(nh.y, nh.x) |= F_REAL; + } + } + else if (on(player, ISHELD) && ch != 'F') + { + msg(rs,"you are being held"); + return; + } + switch (ch) + { + case ' ': + case '|': + case '-': +hit_bound: + if (passgo && running && (proom->r_flags & ISGONE) + && !on(player, ISBLIND)) + { + bool b1, b2; + + switch (runch) + { + case 'h': + case 'l': + b1 = (bool)(hero.y != 1 && turn_ok(hero.y - 1, hero.x)); + b2 = (bool)(hero.y != NUMLINES - 2 && turn_ok(hero.y + 1, hero.x)); + if (!(b1 ^ b2)) + break; + if (b1) + { + runch = 'k'; + dy = -1; + } + else + { + runch = 'j'; + dy = 1; + } + dx = 0; + turnref(); + goto over; + case 'j': + case 'k': + b1 = (bool)(hero.x != 0 && turn_ok(hero.y, hero.x - 1)); + b2 = (bool)(hero.x != NUMCOLS - 1 && turn_ok(hero.y, hero.x + 1)); + if (!(b1 ^ b2)) + break; + if (b1) + { + runch = 'h'; + dx = -1; + } + else + { + runch = 'l'; + dx = 1; + } + dy = 0; + turnref(); + goto over; + } + } + running = FALSE; + after = FALSE; + break; + case DOOR: + running = FALSE; + if (flat(hero.y, hero.x) & F_PASS) + enter_room(rs,&nh); + goto move_stuff; + case TRAP: + ch = be_trapped(rs,&nh); + if (ch == T_DOOR || ch == T_TELEP) + return; + goto move_stuff; + case PASSAGE: + /* + * when you're in a corridor, you don't know if you're in + * a maze room or not, and there ain't no way to find out + * if you're leaving a maze room, so it is necessary to + * always recalculate proom. + */ + proom = roomin(rs,&hero); + goto move_stuff; + case FLOOR: + if (!(fl & F_REAL)) + be_trapped(rs,&hero); + goto move_stuff; + case STAIRS: + seenstairs = TRUE; + /* FALLTHROUGH */ + default: + running = FALSE; + if (isupper(ch) || moat(nh.y, nh.x)) + fight(rs,&nh, cur_weapon, FALSE); + else + { + if (ch != STAIRS) + take = ch; +move_stuff: + mvaddch(hero.y, hero.x, floor_at()); + if ((fl & F_PASS) && chat(oldpos.y, oldpos.x) == DOOR) + leave_room(rs,&nh); + hero = nh; + } + } +} + +/* + * turn_ok: + * Decide whether it is legal to turn onto the given space + */ +bool +turn_ok(int y, int x) +{ + PLACE *pp; + + pp = INDEX(y, x); + return (pp->p_ch == DOOR + || (pp->p_flags & (F_REAL|F_PASS)) == (F_REAL|F_PASS)); +} + +/* + * turnref: + * Decide whether to refresh at a passage turning or not + */ + +void +turnref() +{ + PLACE *pp; + + pp = INDEX(hero.y, hero.x); + if (!(pp->p_flags & F_SEEN)) + { + if (jump) + { + leaveok(stdscr, TRUE); + if ( globalR.sleeptime != 0 ) + refresh(); + leaveok(stdscr, FALSE); + } + pp->p_flags |= F_SEEN; + } +} + +/* + * door_open: + * Called to illuminate a room. If it is dark, remove anything + * that might move. + */ + +void +door_open(struct rogue_state *rs,struct room *rp) +{ + int y, x; + + if (!(rp->r_flags & ISGONE)) + for (y = rp->r_pos.y; y < rp->r_pos.y + rp->r_max.y; y++) + for (x = rp->r_pos.x; x < rp->r_pos.x + rp->r_max.x; x++) + if (isupper(winat(y, x))) + wake_monster(rs,y, x); +} + +/* + * be_trapped: + * The guy stepped on a trap.... Make him pay. + */ +char +be_trapped(struct rogue_state *rs,coord *tc) +{ + PLACE *pp; + THING *arrow; + char tr; + + if (on(player, ISLEVIT)) + return T_RUST; /* anything that's not a door or teleport */ + running = FALSE; + count = FALSE; + pp = INDEX(tc->y, tc->x); + pp->p_ch = TRAP; + tr = pp->p_flags & F_TMASK; + pp->p_flags |= F_SEEN; + switch (tr) + { + case T_DOOR: + level++; + new_level(rs); + msg(rs,"you fell into a trap!"); + when T_BEAR: + no_move += BEARTIME; + msg(rs,"you are caught in a bear trap"); + when T_MYST: + switch(rnd(11)) + { + case 0: msg(rs,"you are suddenly in a parallel dimension"); + when 1: msg(rs,"the light in here suddenly seems %s", rainbow[rnd(cNCOLORS)]); + when 2: msg(rs,"you feel a sting in the side of your neck"); + when 3: msg(rs,"multi-colored lines swirl around you, then fade"); + when 4: msg(rs,"a %s light flashes in your eyes", rainbow[rnd(cNCOLORS)]); + when 5: msg(rs,"a spike shoots past your ear!"); + when 6: msg(rs,"%s sparks dance across your armor", rainbow[rnd(cNCOLORS)]); + when 7: msg(rs,"you suddenly feel very thirsty"); + when 8: msg(rs,"you feel time speed up suddenly"); + when 9: msg(rs,"time now seems to be going slower"); + when 10: msg(rs,"you pack turns %s!", rainbow[rnd(cNCOLORS)]); + } + when T_SLEEP: + no_command += SLEEPTIME; + player.t_flags &= ~ISRUN; + msg(rs,"a strange white mist envelops you and you fall asleep"); + when T_ARROW: + if (swing(pstats.s_lvl - 1, pstats.s_arm, 1)) + { + pstats.s_hpt -= roll(1, 6); + if (pstats.s_hpt <= 0) + { + msg(rs,"an arrow killed you"); + death(rs,'a'); + } + else + msg(rs,"oh no! An arrow shot you"); + } + else + { + arrow = new_item(); + init_weapon(arrow, ARROW); + arrow->o_count = 1; + arrow->o_pos = hero; + fall(rs,arrow, FALSE); + msg(rs,"an arrow shoots past you"); + } + when T_TELEP: + /* + * since the hero's leaving, look() won't put a TRAP + * down for us, so we have to do it ourself + */ + teleport(rs); + mvaddch(tc->y, tc->x, TRAP); + when T_DART: + if (!swing(pstats.s_lvl+1, pstats.s_arm, 1)) + msg(rs,"a small dart whizzes by your ear and vanishes"); + else + { + pstats.s_hpt -= roll(1, 4); + if (pstats.s_hpt <= 0) + { + msg(rs,"a poisoned dart killed you"); + death(rs,'d'); + } + if (!ISWEARING(R_SUSTSTR) && !save(VS_POISON)) + chg_str(-1); + msg(rs,"a small dart just hit you in the shoulder"); + } + when T_RUST: + msg(rs,"a gush of water hits you on the head"); + rust_armor(rs,cur_armor); + } + flush_type(); + return tr; +} + +/* + * rndmove: + * Move in a random direction if the monster/person is confused + */ +coord * +rndmove(THING *who) +{ + THING *obj; + int x, y; + char ch; + static coord ret; /* what we will be returning */ + + y = ret.y = who->t_pos.y + rnd(3) - 1; + x = ret.x = who->t_pos.x + rnd(3) - 1; + /* + * Now check to see if that's a legal move. If not, don't move. + * (I.e., bump into the wall or whatever) + */ + if (y == who->t_pos.y && x == who->t_pos.x) + return &ret; + if (!diag_ok(&who->t_pos, &ret)) + goto bad; + else + { + ch = winat(y, x); + if (!step_ok(ch)) + goto bad; + if (ch == SCROLL) + { + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + if (y == obj->o_pos.y && x == obj->o_pos.x) + break; + if (obj != NULL && obj->o_which == S_SCARE) + goto bad; + } + } + return &ret; + +bad: + ret = who->t_pos; + return &ret; +} + +/* + * rust_armor: + * Rust the given armor, if it is a legal kind to rust, and we + * aren't wearing a magic ring. + */ + +void +rust_armor(struct rogue_state *rs,THING *arm) +{ + if (arm == NULL || arm->o_type != ARMOR || arm->o_which == LEATHER || + arm->o_arm >= 9) + return; + + if ((arm->o_flags & ISPROT) || ISWEARING(R_SUSTARM)) + { + if (!to_death) + msg(rs,"the rust vanishes instantly"); + } + else + { + arm->o_arm++; + if (!terse) + msg(rs,"your armor appears to be weaker now. Oh my!"); + else + msg(rs,"your armor weakens"); + } +} diff --git a/src/cc/rogue/new_level.c b/src/cc/rogue/new_level.c new file mode 100644 index 000000000..c7f08ff28 --- /dev/null +++ b/src/cc/rogue/new_level.c @@ -0,0 +1,244 @@ +/* + * new_level: + * Dig and draw a new level + * + * @(#)new_level.c 4.38 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" + +#define TREAS_ROOM 20 /* one chance in TREAS_ROOM for a treasure room */ +#define MAXTREAS 10 /* maximum number of treasures in a treasure room */ +#define MINTREAS 2 /* minimum number of treasures in a treasure room */ + +void +new_level(struct rogue_state *rs) +{ + THING *tp; + PLACE *pp; + char *sp; + int i; + if ( 0 ) + { + static FILE *fp; + if ( fp == 0 ) + fp = fopen("debug","wb"); + if ( fp != 0 ) + { + fprintf(fp,"newlevel seed.%llu\n",(long long)seed); + fflush(fp); + } + } + player.t_flags &= ~ISHELD; /* unhold when you go down just in case */ + if (level > max_level) + max_level = level; + /* + * Clean things off from last level + */ + for (pp = places; pp < &places[MAXCOLS*MAXLINES]; pp++) + { + pp->p_ch = ' '; + pp->p_flags = F_REAL; + pp->p_monst = NULL; + } + clear(); + /* + * Free up the monsters on the last level + */ + for (tp = mlist; tp != NULL; tp = next(tp)) + free_list(tp->t_pack); + free_list(mlist); + /* + * Throw away stuff left on the previous level (if anything) + */ + free_list(lvl_obj); + do_rooms(rs); /* Draw rooms */ + do_passages(rs); /* Draw passages */ + no_food++; + //fprintf(stderr,"new_level.%d\n",level); + put_things(rs); /* Place objects (if any) */ + /* + * Place the traps + */ + if (rnd(10) < level) + { + ntraps = rnd(level / 4) + 1; + if (ntraps > MAXTRAPS) + ntraps = MAXTRAPS; + i = ntraps; + while (i--) + { + /* + * not only wouldn't it be NICE to have traps in mazes + * (not that we care about being nice), since the trap + * number is stored where the passage number is, we + * can't actually do it. + */ + do + { + find_floor(rs,(struct room *) NULL, &stairs, FALSE, FALSE); + } while (chat(stairs.y, stairs.x) != FLOOR); + sp = &flat(stairs.y, stairs.x); + *sp &= ~F_REAL; + *sp |= rnd(NTRAPS); + } + } + /* + * Place the staircase down. + */ + find_floor(rs,(struct room *) NULL, &stairs, FALSE, FALSE); + chat(stairs.y, stairs.x) = STAIRS; + seenstairs = FALSE; + + for (tp = mlist; tp != NULL; tp = next(tp)) + tp->t_room = roomin(rs,&tp->t_pos); + + find_floor(rs,(struct room *) NULL, &hero, FALSE, TRUE); + enter_room(rs,&hero); + mvaddch(hero.y, hero.x, PLAYER); + if (on(player, SEEMONST)) + turn_see(rs,FALSE); + if (on(player, ISHALU)) + visuals(rs,0); +} + +/* + * rnd_room: + * Pick a room that is really there + */ +int +rnd_room() +{ + int rm; + + do + { + rm = rnd(MAXROOMS); + } while (rooms[rm].r_flags & ISGONE); + return rm; +} + +/* + * put_things: + * Put potions and scrolls on this level + */ + +void +put_things(struct rogue_state *rs) +{ + int i; + THING *obj; + + /* + * Once you have found the amulet, the only way to get new stuff is + * go down into the dungeon. + */ + if (amulet && level < max_level) + return; + /* + * check for treasure rooms, and if so, put it in. + */ + if (rnd(TREAS_ROOM) == 0) + treas_room(rs); + /* + * Do MAXOBJ attempts to put things on a level + */ + for (i = 0; i < MAXOBJ; i++) + if (rnd(100) < 36) + { + /* + * Pick a new object and link it in the list + */ + obj = new_thing(rs); + //fprintf(stderr,"put_things i.%d obj.%p\n",i,obj); + attach(lvl_obj, obj); + /* + * Put it somewhere + */ + find_floor(rs,(struct room *) NULL, &obj->o_pos, FALSE, FALSE); + chat(obj->o_pos.y, obj->o_pos.x) = (char) obj->o_type; + } + /* + * If he is really deep in the dungeon and he hasn't found the + * amulet yet, put it somewhere on the ground + */ + if (level >= AMULETLEVEL && !amulet) + { + obj = new_item(); + attach(lvl_obj, obj); + obj->o_hplus = 0; + obj->o_dplus = 0; + strncpy(obj->o_damage,"0x0",sizeof(obj->o_damage)); + strncpy(obj->o_hurldmg,"0x0",sizeof(obj->o_hurldmg)); + obj->o_arm = 11; + obj->o_type = AMULET; + /* + * Put it somewhere + */ + find_floor(rs,(struct room *) NULL, &obj->o_pos, FALSE, FALSE); + chat(obj->o_pos.y, obj->o_pos.x) = AMULET; + } +} + +/* + * treas_room: + * Add a treasure room + */ +#define MAXTRIES 10 /* max number of tries to put down a monster */ + + +void +treas_room(struct rogue_state *rs) +{ + int nm; + THING *tp; + struct room *rp; + int spots, num_monst; + static coord mp; + + rp = &rooms[rnd_room()]; + spots = (rp->r_max.y - 2) * (rp->r_max.x - 2) - MINTREAS; + if (spots > (MAXTREAS - MINTREAS)) + spots = (MAXTREAS - MINTREAS); + num_monst = nm = rnd(spots) + MINTREAS; + while (nm--) + { + find_floor(rs,rp, &mp, 2 * MAXTRIES, FALSE); + //fprintf(stderr,"treas_room\n"); + tp = new_thing(rs); + tp->o_pos = mp; + attach(lvl_obj, tp); + chat(mp.y, mp.x) = (char) tp->o_type; + } + + /* + * fill up room with monsters from the next level down + */ + + if ((nm = rnd(spots) + MINTREAS) < num_monst + 2) + nm = num_monst + 2; + spots = (rp->r_max.y - 2) * (rp->r_max.x - 2); + if (nm > spots) + nm = spots; + level++; + while (nm--) + { + spots = 0; + if (find_floor(rs,rp, &mp, MAXTRIES, TRUE)) + { + tp = new_item(); + new_monster(rs,tp, randmonster(FALSE), &mp); + tp->t_flags |= ISMEAN; /* no sloughers in THIS room */ + give_pack(rs,tp); + } + } + level--; +} diff --git a/src/cc/rogue/options.c b/src/cc/rogue/options.c new file mode 100644 index 000000000..258bd184f --- /dev/null +++ b/src/cc/rogue/options.c @@ -0,0 +1,501 @@ +/* + * This file has all the code for the option command. I would rather + * this command were not necessary, but it is the only way to keep the + * wolves off of my back. + * + * @(#)options.c 4.24 (Berkeley) 05/10/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +//#include +#include "rogue.h" + +#define EQSTR(a, b, c) (strncmp(a, b, c) == 0) + +#define NUM_OPTS (sizeof optlist / sizeof (OPTION)) + +/* + * description of an option and what to do with it + */ +struct optstruct { + char *o_name; /* option name */ + char *o_prompt; /* prompt for interactive entry */ + void *o_opt; /* pointer to thing to set */ + /* function to print value */ + void (*o_putfunc)(void *opt); + /* function to get value interactively */ + int (*o_getfunc)(struct rogue_state *rs,void *opt, WINDOW *win); +}; + +typedef struct optstruct OPTION; + +void pr_optname(OPTION *op); + +OPTION optlist[] = { + {"terse", "Terse output", + &terse, put_bool, get_bool }, + {"flush", "Flush typeahead during battle", + &fight_flush, put_bool, get_bool }, + {"jump", "Show position only at end of run", + &jump, put_bool, get_bool }, + {"seefloor", "Show the lamp-illuminated floor", + &see_floor, put_bool, get_sf }, + {"passgo", "Follow turnings in passageways", + &passgo, put_bool, get_bool }, + {"tombstone", "Print out tombstone when killed", + &tombstone, put_bool, get_bool }, + {"inven", "Inventory style", + &inv_type, put_inv_t, get_inv_t }, + {"name", "Name", + whoami, put_str, get_str }, + {"fruit", "Fruit", + fruit, put_str, get_str }, + {"file", "Save file", + file_name, put_str, get_str } +}; + +/* + * option: + * Print and then set options from the terminal + */ + +void +option(struct rogue_state *rs) +{ + OPTION *op; + int retval; + + wclear(hw); + /* + * Display current values of options + */ + for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++) + { + pr_optname(op); + (*op->o_putfunc)(op->o_opt); + waddch(hw, '\n'); + } + /* + * Set values + */ + wmove(hw, 0, 0); + for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++) + { + pr_optname(op); + retval = (*op->o_getfunc)(rs,op->o_opt, hw); + if (retval) + { + if (retval == QUIT) + break; + else if (op > optlist) { /* MINUS */ + wmove(hw, (int)(op - optlist) - 1, 0); + op -= 2; + } + else /* trying to back up beyond the top */ + { + putchar('\007'); + wmove(hw, 0, 0); + op--; + } + } + } + /* + * Switch back to original screen + */ + wmove(hw, LINES - 1, 0); + waddstr(hw, "--Press space to continue--"); + wrefresh(hw); + wait_for(rs,' '); + clearok(curscr, TRUE); + touchwin(stdscr); + after = FALSE; +} + +/* + * pr_optname: + * Print out the option name prompt + */ + +void +pr_optname(OPTION *op) +{ + wprintw(hw, "%s (\"%s\"): ", op->o_prompt, op->o_name); +} + +/* + * put_bool + * Put out a boolean + */ + +void +put_bool(void *b) +{ + waddstr(hw, *(bool *) b ? "True" : "False"); +} + +/* + * put_str: + * Put out a string + */ + +void +put_str(void *str) +{ + waddstr(hw, (char *) str); +} + +/* + * put_inv_t: + * Put out an inventory type + */ + +void +put_inv_t(void *ip) +{ + waddstr(hw, inv_t_name[*(int *) ip]); +} + +/* + * get_bool: + * Allow changing a boolean option and print it out + */ +int +get_bool(struct rogue_state *rs,void *vp, WINDOW *win) +{ + bool *bp = (bool *) vp; + int oy, ox; + bool op_bad; + + op_bad = TRUE; + getyx(win, oy, ox); + waddstr(win, *bp ? "True" : "False"); + while (op_bad) + { + wmove(win, oy, ox); + wrefresh(win); + switch (readchar(rs)) + { + case 't': + case 'T': + *bp = TRUE; + op_bad = FALSE; + break; + case 'f': + case 'F': + *bp = FALSE; + op_bad = FALSE; + break; + case '\n': + case '\r': + op_bad = FALSE; + break; + case ESCAPE: + return QUIT; + case '-': + return MINUS; + default: + wmove(win, oy, ox + 10); + waddstr(win, "(T or F)"); + } + } + wmove(win, oy, ox); + waddstr(win, *bp ? "True" : "False"); + waddch(win, '\n'); + return NORM; +} + +/* + * get_sf: + * Change value and handle transition problems from see_floor to + * !see_floor. + */ +int +get_sf(struct rogue_state *rs,void *vp, WINDOW *win) +{ + bool *bp = (bool *) vp; + bool was_sf; + int retval; + + was_sf = see_floor; + retval = get_bool(rs,bp, win); + if (retval == QUIT) return(QUIT); + if (was_sf != see_floor) + { + if (!see_floor) { + see_floor = TRUE; + erase_lamp(&hero, proom); + see_floor = FALSE; + } + else + look(rs,FALSE); + } + return(NORM); +} + +/* + * get_str: + * Set a string option + */ +#define MAXINP 50 /* max string to read from terminal or environment */ + +int +get_str(struct rogue_state *rs,void *vopt, WINDOW *win) +{ + char *opt = (char *) vopt; + char *sp; + int oy, ox; + int i; + signed char c; + static char buf[MAXSTR]; + + getyx(win, oy, ox); + wrefresh(win); + /* + * loop reading in the string, and put it in a temporary buffer + */ + for (sp = buf; (c = readchar(rs)) != '\n' && c != '\r' && c != ESCAPE; + wclrtoeol(win), wrefresh(win)) + { + if (c == -1) + continue; + /*else if (c == erasechar()) // process erase character + { + if (sp > buf) + { + sp--; + for (i = (int) strlen(unctrl(*sp)); i; i--) + waddch(win, '\b'); + } + continue; + } + else if (c == killchar()) // process kill character + { + sp = buf; + wmove(win, oy, ox); + continue; + }*/ + else if (sp == buf) + { + if (c == '-' && win != stdscr) + break; + else if (c == '~') + { + strcpy(buf, home); + waddstr(win, home); + sp += strlen(home); + continue; + } + } + if (sp >= &buf[MAXINP] || !(isprint(c) || c == ' ')) + putchar(CTRL('G')); + else + { + *sp++ = c; + waddstr(win, unctrl(c)); + } + } + *sp = '\0'; + if (sp > buf) /* only change option if something has been typed */ + strucpy(opt, buf, (int) strlen(buf)); + mvwprintw(win, oy, ox, "%s\n", opt); + wrefresh(win); + if (win == stdscr) + mpos += (int)(sp - buf); + if (c == '-') + return MINUS; + else if (c == ESCAPE) + return QUIT; + else + return NORM; +} + +/* + * get_inv_t + * Get an inventory type name + */ +int +get_inv_t(struct rogue_state *rs,void *vp, WINDOW *win) +{ + int *ip = (int *) vp; + int oy, ox; + bool op_bad; + + op_bad = TRUE; + getyx(win, oy, ox); + waddstr(win, inv_t_name[*ip]); + while (op_bad) + { + wmove(win, oy, ox); + wrefresh(win); + switch (readchar(rs)) + { + case 'o': + case 'O': + *ip = INV_OVER; + op_bad = FALSE; + break; + case 's': + case 'S': + *ip = INV_SLOW; + op_bad = FALSE; + break; + case 'c': + case 'C': + *ip = INV_CLEAR; + op_bad = FALSE; + break; + case '\n': + case '\r': + op_bad = FALSE; + break; + case ESCAPE: + return QUIT; + case '-': + return MINUS; + default: + wmove(win, oy, ox + 15); + waddstr(win, "(O, S, or C)"); + } + } + mvwprintw(win, oy, ox, "%s\n", inv_t_name[*ip]); + return NORM; +} + + +#ifdef MASTER +/* + * get_num: + * Get a numeric option + */ +int +get_num(struct rogue_state *rs,void *vp, WINDOW *win) +{ + short *opt = (short *) vp; + int i; + static char buf[MAXSTR]; + + if ((i = get_str(rs,buf, win)) == NORM) + *opt = (short) atoi(buf); + return i; +} +#endif + +/* + * parse_opts: + * Parse options from string, usually taken from the environment. + * The string is a series of comma seperated values, with booleans + * being stated as "name" (true) or "noname" (false), and strings + * being "name=....", with the string being defined up to a comma + * or the end of the entire option string. + */ + +void +parse_opts(char *str) +{ + char *sp; + OPTION *op; + int len; + const char **i; + char *start; + + while (*str) + { + /* + * Get option name + */ + for (sp = str; isalpha(*sp); sp++) + continue; + len = (int)(sp - str); + /* + * Look it up and deal with it + */ + for (op = optlist; op <= &optlist[NUM_OPTS-1]; op++) + if (EQSTR(str, op->o_name, len)) + { + if (op->o_putfunc == put_bool) /* if option is a boolean */ + *(bool *)op->o_opt = TRUE; /* NOSTRICT */ + else /* string option */ + { + /* + * Skip to start of string value + */ + for (str = sp + 1; *str == '='; str++) + continue; + if (*str == '~') + { + strcpy((char *) op->o_opt, home); /* NOSTRICT */ + start = (char *) op->o_opt + strlen(home);/* NOSTRICT */ + while (*++str == '/') + continue; + } + else + start = (char *) op->o_opt; /* NOSTRICT */ + /* + * Skip to end of string value + */ + for (sp = str + 1; *sp && *sp != ','; sp++) + continue; + /* + * check for type of inventory + */ + if (op->o_putfunc == put_inv_t) + { + if (islower(*str)) + *str = (char) toupper(*str); + for (i = inv_t_name; i <= &inv_t_name[INV_CLEAR]; i++) + if (strncmp(str, *i, sp - str) == 0) + { + inv_type = (int)(i - inv_t_name); + break; + } + } + else + strucpy(start, str, (int)(sp - str)); + } + break; + } + /* + * check for "noname" for booleans + */ + else if (op->o_putfunc == put_bool + && EQSTR(str, "no", 2) && EQSTR(str + 2, op->o_name, len - 2)) + { + *(bool *)op->o_opt = FALSE; /* NOSTRICT */ + break; + } + + /* + * skip to start of next option name + */ + while (*sp && !isalpha(*sp)) + sp++; + str = sp; + } +} + +/* + * strucpy: + * Copy string using unctrl for things + */ + +void +strucpy(char *s1, char *s2, int len) +{ + if (len > MAXINP) + len = MAXINP; + while (len--) + { + if (isprint(*s2) || *s2 == ' ') + *s1++ = *s2; + s2++; + } + *s1 = '\0'; +} diff --git a/src/cc/rogue/pack.c b/src/cc/rogue/pack.c new file mode 100644 index 000000000..1f2f3faaa --- /dev/null +++ b/src/cc/rogue/pack.c @@ -0,0 +1,540 @@ +/* + * Routines to deal with the pack + * + * @(#)pack.c 4.40 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +#include "rogue.h" + +/* + * add_pack: + * Pick up an object and add it to the pack. If the argument is + * non-null use it as the linked_list pointer instead of gettting + * it off the ground. + */ + +void +add_pack(struct rogue_state *rs,THING *obj, bool silent) +{ + THING *op, *lp; + bool from_floor; + + from_floor = FALSE; + if (obj == NULL) + { + if ((obj = find_obj(rs,hero.y, hero.x)) == NULL) + return; + from_floor = TRUE; + } + + /* + * Check for and deal with scare monster scrolls + */ + if (obj->o_type == SCROLL && obj->o_which == S_SCARE) + if (obj->o_flags & ISFOUND) + { + detach(lvl_obj, obj); + mvaddch(hero.y, hero.x, floor_ch()); + chat(hero.y, hero.x) = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR; + discard(obj); + msg(rs,"the scroll turns to dust as you pick it up"); + return; + } + + if (pack == NULL) + { + pack = obj; + obj->o_packch = pack_char(); + inpack++; + } + else + { + lp = NULL; + for (op = pack; op != NULL; op = next(op)) + { + if (op->o_type != obj->o_type) + lp = op; + else + { + while (op->o_type == obj->o_type && op->o_which != obj->o_which) + { + lp = op; + if (next(op) == NULL) + break; + else + op = next(op); + } + if (op->o_type == obj->o_type && op->o_which == obj->o_which) + { + if (ISMULT(op->o_type)) + { + if (!pack_room(rs,from_floor, obj)) + return; + op->o_count++; +dump_it: + discard(obj); + obj = op; + lp = NULL; + goto out; + } + else if (obj->o_group) + { + lp = op; + while (op->o_type == obj->o_type + && op->o_which == obj->o_which + && op->o_group != obj->o_group) + { + lp = op; + if (next(op) == NULL) + break; + else + op = next(op); + } + if (op->o_type == obj->o_type + && op->o_which == obj->o_which + && op->o_group == obj->o_group) + { + op->o_count += obj->o_count; + inpack--; + if (!pack_room(rs,from_floor, obj)) + return; + goto dump_it; + } + } + else + lp = op; + } +out: + break; + } + } + + if (lp != NULL) + { + if (!pack_room(rs,from_floor, obj)) + return; + else + { + obj->o_packch = pack_char(); + next(obj) = next(lp); + prev(obj) = lp; + if (next(lp) != NULL) + prev(next(lp)) = obj; + next(lp) = obj; + } + } + } + + obj->o_flags |= ISFOUND; + + /* + * If this was the object of something's desire, that monster will + * get mad and run at the hero. + */ + for (op = mlist; op != NULL; op = next(op)) + if (op->t_dest == &obj->o_pos) + op->t_dest = &hero; + + if (obj->o_type == AMULET) + amulet = TRUE; + /* + * Notify the user + */ + if (!silent) + { + if (!terse) + addmsg(rs,"you now have "); + msg(rs,"%s (%c)", inv_name(obj, !terse), obj->o_packch); + } +} + +int32_t num_packitems(struct rogue_state *rs) +{ + THING *list = pack; + int32_t type = 0,n = 0,total = 0; + for (; list != NULL; list = next(list)) + { + if ( thing_find(list) < 0 ) + { + fprintf(stderr,"num_packitems cant find %p\n",list); + return(-1); + } + if ( list->o_packch != 0 ) + { + n++; + total += rogue_total(list); + } + } + if ( rs->guiflag != 0 ) + { + char str[MAXSTR]; + sprintf(str,"strength*2 %d vs total.%d vs %d inventory letters\n",ROGUE_MAXTOTAL,total,n); + add_line(rs,"%s",str); + } + if ( total > ROGUE_MAXTOTAL ) + return(MAXPACK); + return(n); +} + +/* + * pack_room: + * See if there's room in the pack. If not, print out an + * appropriate message + */ +bool pack_room(struct rogue_state *rs,bool from_floor, THING *obj) +{ + inpack = num_packitems(rs); + if ( ++inpack > MAXPACK ) + { + if (!terse) + addmsg(rs,"there's "); + addmsg(rs,"no room"); + if (!terse) + addmsg(rs," in your pack"); + endmsg(rs); + if (from_floor) + move_msg(rs,obj); + inpack = MAXPACK; + return FALSE; + } + //fprintf(stderr,"inpack.%d vs MAX.%d\n",inpack,MAXPACK), sleep(2); + if ( from_floor != 0 ) + { + detach(lvl_obj, obj); + mvaddch(hero.y, hero.x, floor_ch()); + chat(hero.y, hero.x) = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR; + } + return TRUE; +} + +/* + * leave_pack: + * take an item out of the pack + */ +THING * +leave_pack(struct rogue_state *rs,THING *obj, bool newobj, bool all) +{ + THING *nobj; + + inpack--; + nobj = obj; + if (obj->o_count > 1 && !all) + { + last_pick = obj; + obj->o_count--; + if (obj->o_group) + inpack++; + if (newobj) + { + nobj = new_item(); + *nobj = *obj; + next(nobj) = NULL; + prev(nobj) = NULL; + nobj->o_count = 1; + } + } + else + { + last_pick = NULL; + pack_used[obj->o_packch - 'a'] = FALSE; + detach(pack, obj); + } + return nobj; +} + +/* + * pack_char: + * Return the next unused pack character. + */ +char +pack_char() +{ + bool *bp; + + for (bp = pack_used; *bp; bp++) + continue; + *bp = TRUE; + return (char)((int)(bp - pack_used) + 'a'); +} + +/* + * inventory: + * List what is in the pack. Return TRUE if there is something of + * the given type. + */ + + +bool +inventory(struct rogue_state *rs,THING *list, int type) +{ + char inv_temp[MAXSTR]; + + n_objs = 0; + for (; list != NULL; list = next(list)) + { + if (type && type != list->o_type && !(type == CALLABLE && + list->o_type != FOOD && list->o_type != AMULET) && + !(type == R_OR_S && (list->o_type == RING || list->o_type == STICK))) + continue; + n_objs++; +#ifdef MASTER + if (!list->o_packch) + strcpy(inv_temp, "%s"); + else +#endif + sprintf(inv_temp, "%c) %%s", list->o_packch); + msg_esc = TRUE; + if (add_line(rs,inv_temp, inv_name(list, FALSE)) == ESCAPE) + { + msg_esc = FALSE; + msg(rs,""); + return TRUE; + } + msg_esc = FALSE; + } + //if ( n_objs != inpack ) + // fprintf(stderr,"n_objs.%d vs inpack.%d\n",n_objs,inpack), sleep(2); + if (n_objs == 0) + { + if (terse) + msg(rs,type == 0 ? (char *)"empty handed" : (char *)"nothing appropriate"); + else + msg(rs,type == 0 ? (char *)"you are empty handed" : (char *)"you don't have anything appropriate"); + return FALSE; + } + end_line(rs); + return TRUE; +} + +/* + * pick_up: + * Add something to characters pack. + */ + +void +pick_up(struct rogue_state *rs,char ch) +{ + THING *obj; + + if (on(player, ISLEVIT)) + return; + + obj = find_obj(rs,hero.y, hero.x); + if (move_on) + move_msg(rs,obj); + else + switch (ch) + { + case GOLD: + if (obj == NULL) + return; + money(rs,obj->o_goldval); + detach(lvl_obj, obj); + discard(obj); + proom->r_goldval = 0; + break; + default: +#ifdef MASTER + debug("Where did you pick a '%s' up???", unctrl(ch)); +#endif + case ARMOR: + case POTION: + case FOOD: + case WEAPON: + case SCROLL: + case AMULET: + case RING: + case STICK: + add_pack(rs,(THING *) NULL, FALSE); + break; + } +} + +/* + * move_msg: + * Print out the message if you are just moving onto an object + */ + +void +move_msg(struct rogue_state *rs,THING *obj) +{ + if (!terse) + addmsg(rs,"you "); + msg(rs,"moved onto %s", inv_name(obj, TRUE)); +} + +/* + * picky_inven: + * Allow player to inventory a single item + */ + +void +picky_inven(struct rogue_state *rs) +{ + THING *obj; + char mch; + + if (pack == NULL) + msg(rs,"you aren't carrying anything"); + else if (next(pack) == NULL) + msg(rs,"a) %s", inv_name(pack, FALSE)); + else + { + msg(rs,terse ? (char *)"item: " : (char *)"which item do you wish to inventory: "); + mpos = 0; + if ((mch = readchar(rs)) == ESCAPE) + { + msg(rs,""); + return; + } + for (obj = pack; obj != NULL; obj = next(obj)) + if (mch == obj->o_packch) + { + msg(rs,"%c) %s", mch, inv_name(obj, FALSE)); + return; + } + msg(rs,"'%s' not in pack", unctrl(mch)); + } +} + +/* + * get_item: + * Pick something out of a pack for a purpose + */ +THING * +get_item(struct rogue_state *rs,char *purpose, int type) +{ + THING *obj; + char ch; + + if (pack == NULL) + msg(rs,"you aren't carrying anything"); + else if (again) + if (last_pick) + return last_pick; + else + msg(rs,"you ran out"); + else + { + for (;;) + { + if ( rs->replaydone != 0 ) + return(NULL); + if (!terse) + addmsg(rs,"which object do you want to "); + addmsg(rs,purpose); + if (terse) + addmsg(rs," what"); + msg(rs,"? (* for list): "); + ch = readchar(rs); + mpos = 0; + /* + * Give the poor player a chance to abort the command + */ + if (ch == ESCAPE) + { + reset_last(); + after = FALSE; + msg(rs,""); + return NULL; + } + n_objs = 1; /* normal case: person types one char */ + if (ch == '*') + { + mpos = 0; + if (inventory(rs,pack, type) == 0) + { + after = FALSE; + return NULL; + } + continue; + } + for (obj = pack; obj != NULL; obj = next(obj)) + if (obj->o_packch == ch) + break; + if (obj == NULL) + { + //msg(rs,"'%s' is not a valid item",unctrl(ch)); + //continue; + reset_last(); + after = FALSE; + msg(rs,"'%s' is not a valid item",unctrl(ch)); + return NULL; + } + else + { + return obj; + } + } + } + return NULL; +} + +/* + * money: + * Add or subtract gold from the pack + */ + +void +money(struct rogue_state *rs,int value) +{ + purse += value; + mvaddch(hero.y, hero.x, floor_ch()); + chat(hero.y, hero.x) = (proom->r_flags & ISGONE) ? PASSAGE : FLOOR; + if (value > 0) + { + if (!terse) + addmsg(rs,"you found "); + msg(rs,"%d gold pieces", value); + } +} + +/* + * floor_ch: + * Return the appropriate floor character for her room + */ +char +floor_ch() +{ + if (proom->r_flags & ISGONE) + return PASSAGE; + return (show_floor() ? FLOOR : ' '); +} + +/* + * floor_at: + * Return the character at hero's position, taking see_floor + * into account + */ +char +floor_at() +{ + char ch; + + ch = chat(hero.y, hero.x); + if (ch == FLOOR) + ch = floor_ch(); + return ch; +} + +/* + * reset_last: + * Reset the last command when the current one is aborted + */ + +void +reset_last() +{ + last_comm = l_last_comm; + last_dir = l_last_dir; + last_pick = l_last_pick; +} diff --git a/src/cc/rogue/passages.c b/src/cc/rogue/passages.c new file mode 100644 index 000000000..606d1a658 --- /dev/null +++ b/src/cc/rogue/passages.c @@ -0,0 +1,424 @@ +/* + * Draw the connecting passages + * + * @(#)passages.c 4.22 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" + +/* + * do_passages: + * Draw all the passages on a level. + */ + +void +do_passages(struct rogue_state *rs) +{ + struct rdes *r1, *r2 = NULL; + int i, j; + int roomcount; + static struct rdes + { + bool conn[MAXROOMS]; /* possible to connect to room i? */ + bool isconn[MAXROOMS]; /* connection been made to room i? */ + bool ingraph; /* this room in graph already? */ + } rdes[MAXROOMS] = { + { { 0, 1, 0, 1, 0, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 1, 0, 1, 0, 1, 0, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 1, 0, 0, 0, 1, 0, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 1, 0, 0, 0, 1, 0, 1, 0, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 1, 0, 1, 0, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 1, 0, 1, 0, 0, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 0, 1, 0, 0, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 0, 0, 1, 0, 1, 0, 1 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { { 0, 0, 0, 0, 0, 1, 0, 1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + }; + + /* + * reinitialize room graph description + */ + for (r1 = rdes; r1 <= &rdes[MAXROOMS-1]; r1++) + { + for (j = 0; j < MAXROOMS; j++) + r1->isconn[j] = FALSE; + r1->ingraph = FALSE; + } + + /* + * starting with one room, connect it to a random adjacent room and + * then pick a new room to start with. + */ + roomcount = 1; + r1 = &rdes[rnd(MAXROOMS)]; + r1->ingraph = TRUE; + do + { + /* + * find a room to connect with + */ + j = 0; + for (i = 0; i < MAXROOMS; i++) + if (r1->conn[i] && !rdes[i].ingraph && rnd(++j) == 0) + r2 = &rdes[i]; + /* + * if no adjacent rooms are outside the graph, pick a new room + * to look from + */ + if (j == 0) + { + do + r1 = &rdes[rnd(MAXROOMS)]; + until (r1->ingraph); + } + /* + * otherwise, connect new room to the graph, and draw a tunnel + * to it + */ + else + { + r2->ingraph = TRUE; + i = (int)(r1 - rdes); + j = (int)(r2 - rdes); + conn(rs,i, j); + r1->isconn[j] = TRUE; + r2->isconn[i] = TRUE; + roomcount++; + } + } while (roomcount < MAXROOMS); + + /* + * attempt to add passages to the graph a random number of times so + * that there isn't always just one unique passage through it. + */ + for (roomcount = rnd(5); roomcount > 0; roomcount--) + { + r1 = &rdes[rnd(MAXROOMS)]; /* a random room to look from */ + /* + * find an adjacent room not already connected + */ + j = 0; + for (i = 0; i < MAXROOMS; i++) + if (r1->conn[i] && !r1->isconn[i] && rnd(++j) == 0) + r2 = &rdes[i]; + /* + * if there is one, connect it and look for the next added + * passage + */ + if (j != 0) + { + i = (int)(r1 - rdes); + j = (int)(r2 - rdes); + conn(rs,i, j); + r1->isconn[j] = TRUE; + r2->isconn[i] = TRUE; + } + } + passnum(); +} + +/* + * conn: + * Draw a corridor from a room in a certain direction. + */ + +void +conn(struct rogue_state *rs,int r1, int r2) +{ + struct room *rpf, *rpt = NULL; + int rmt; + int distance = 0, turn_spot, turn_distance = 0; + int rm; + char direc; + static coord del, curr, turn_delta, spos, epos; + + if (r1 < r2) + { + rm = r1; + if (r1 + 1 == r2) + direc = 'r'; + else + direc = 'd'; + } + else + { + rm = r2; + if (r2 + 1 == r1) + direc = 'r'; + else + direc = 'd'; + } + rpf = &rooms[rm]; + /* + * Set up the movement variables, in two cases: + * first drawing one down. + */ + if (direc == 'd') + { + rmt = rm + 3; /* room # of dest */ + rpt = &rooms[rmt]; /* room pointer of dest */ + del.x = 0; /* direction of move */ + del.y = 1; + spos.x = rpf->r_pos.x; /* start of move */ + spos.y = rpf->r_pos.y; + epos.x = rpt->r_pos.x; /* end of move */ + epos.y = rpt->r_pos.y; + if (!(rpf->r_flags & ISGONE)) /* if not gone pick door pos */ + do + { + spos.x = rpf->r_pos.x + rnd(rpf->r_max.x - 2) + 1; + spos.y = rpf->r_pos.y + rpf->r_max.y - 1; + } while ((rpf->r_flags&ISMAZE) && !(flat(spos.y, spos.x)&F_PASS)); + if (!(rpt->r_flags & ISGONE)) + do + { + epos.x = rpt->r_pos.x + rnd(rpt->r_max.x - 2) + 1; + } while ((rpt->r_flags&ISMAZE) && !(flat(epos.y, epos.x)&F_PASS)); + distance = abs(spos.y - epos.y) - 1; /* distance to move */ + turn_delta.y = 0; /* direction to turn */ + turn_delta.x = (spos.x < epos.x ? 1 : -1); + turn_distance = abs(spos.x - epos.x); /* how far to turn */ + } + else if (direc == 'r') /* setup for moving right */ + { + rmt = rm + 1; + rpt = &rooms[rmt]; + del.x = 1; + del.y = 0; + spos.x = rpf->r_pos.x; + spos.y = rpf->r_pos.y; + epos.x = rpt->r_pos.x; + epos.y = rpt->r_pos.y; + if (!(rpf->r_flags & ISGONE)) + do + { + spos.x = rpf->r_pos.x + rpf->r_max.x - 1; + spos.y = rpf->r_pos.y + rnd(rpf->r_max.y - 2) + 1; + } while ((rpf->r_flags&ISMAZE) && !(flat(spos.y, spos.x)&F_PASS)); + if (!(rpt->r_flags & ISGONE)) + do + { + epos.y = rpt->r_pos.y + rnd(rpt->r_max.y - 2) + 1; + } while ((rpt->r_flags&ISMAZE) && !(flat(epos.y, epos.x)&F_PASS)); + distance = abs(spos.x - epos.x) - 1; + turn_delta.y = (spos.y < epos.y ? 1 : -1); + turn_delta.x = 0; + turn_distance = abs(spos.y - epos.y); + } +#ifdef MASTER + else + debug("error in connection tables"); +#endif + + turn_spot = rnd(distance - 1) + 1; /* where turn starts */ + + /* + * Draw in the doors on either side of the passage or just put #'s + * if the rooms are gone. + */ + if (!(rpf->r_flags & ISGONE)) + door(rpf, &spos); + else + putpass(&spos); + if (!(rpt->r_flags & ISGONE)) + door(rpt, &epos); + else + putpass(&epos); + /* + * Get ready to move... + */ + curr.x = spos.x; + curr.y = spos.y; + while (distance > 0) + { + /* + * Move to new position + */ + curr.x += del.x; + curr.y += del.y; + /* + * Check if we are at the turn place, if so do the turn + */ + if (distance == turn_spot) + while (turn_distance--) + { + putpass(&curr); + curr.x += turn_delta.x; + curr.y += turn_delta.y; + } + /* + * Continue digging along + */ + putpass(&curr); + distance--; + } + curr.x += del.x; + curr.y += del.y; + if (!ce(curr, epos)) + msg(rs,"warning, connectivity problem on this level"); +} + +/* + * putpass: + * add a passage character or secret passage here + */ + +void +putpass(coord *cp) +{ + PLACE *pp; + + pp = INDEX(cp->y, cp->x); + pp->p_flags |= F_PASS; + if (rnd(10) + 1 < level && rnd(40) == 0) + pp->p_flags &= ~F_REAL; + else + pp->p_ch = PASSAGE; +} + +/* + * door: + * Add a door or possibly a secret door. Also enters the door in + * the exits array of the room. + */ + +void +door(struct room *rm, coord *cp) +{ + PLACE *pp; + + rm->r_exit[rm->r_nexits++] = *cp; + + if (rm->r_flags & ISMAZE) + return; + + pp = INDEX(cp->y, cp->x); + if (rnd(10) + 1 < level && rnd(5) == 0) + { + if (cp->y == rm->r_pos.y || cp->y == rm->r_pos.y + rm->r_max.y - 1) + pp->p_ch = '-'; + else + pp->p_ch = '|'; + pp->p_flags &= ~F_REAL; + } + else + pp->p_ch = DOOR; +} + +#ifdef MASTER +/* + * add_pass: + * Add the passages to the current window (wizard command) + */ + +void +add_pass() +{ + PLACE *pp; + int y, x; + char ch; + + for (y = 1; y < NUMLINES - 1; y++) + for (x = 0; x < NUMCOLS; x++) + { + pp = INDEX(y, x); + if ((pp->p_flags & F_PASS) || pp->p_ch == DOOR || + (!(pp->p_flags&F_REAL) && (pp->p_ch == '|' || pp->p_ch == '-'))) + { + ch = pp->p_ch; + if (pp->p_flags & F_PASS) + ch = PASSAGE; + pp->p_flags |= F_SEEN; + move(y, x); + if (pp->p_monst != NULL) + pp->p_monst->t_oldch = pp->p_ch; + else if (pp->p_flags & F_REAL) + addch(ch); + else + { + standout(); + addch((pp->p_flags & F_PASS) ? PASSAGE : DOOR); + standend(); + } + } + } +} +#endif + +/* + * passnum: + * Assign a number to each passageway + */ +static int pnum; +static bool newpnum; + + +void +passnum() +{ + struct room *rp; + int i; + + pnum = 0; + newpnum = FALSE; + for (rp = passages; rp < &passages[MAXPASS]; rp++) + rp->r_nexits = 0; + for (rp = rooms; rp < &rooms[MAXROOMS]; rp++) + for (i = 0; i < rp->r_nexits; i++) + { + newpnum ^= 1;//newpnum++; + numpass(rp->r_exit[i].y, rp->r_exit[i].x); + } +} + +/* + * numpass: + * Number a passageway square and its brethren + */ + +void +numpass(int y, int x) +{ + char *fp; + struct room *rp; + char ch; + + if (x >= NUMCOLS || x < 0 || y >= NUMLINES || y <= 0) + return; + fp = &flat(y, x); + if (*fp & F_PNUM) + return; + if (newpnum) + { + pnum++; + newpnum = FALSE; + } + /* + * check to see if it is a door or secret door, i.e., a new exit, + * or a numerable type of place + */ + if ((ch = chat(y, x)) == DOOR || + (!(*fp & F_REAL) && (ch == '|' || ch == '-'))) + { + rp = &passages[pnum]; + rp->r_exit[rp->r_nexits].y = y; + rp->r_exit[rp->r_nexits++].x = x; + } + else if (!(*fp & F_PASS)) + return; + *fp |= pnum; + /* + * recurse on the surrounding places + */ + numpass(y + 1, x); + numpass(y - 1, x); + numpass(y, x + 1); + numpass(y, x - 1); +} diff --git a/src/cc/rogue/potions.c b/src/cc/rogue/potions.c new file mode 100644 index 000000000..2425b051f --- /dev/null +++ b/src/cc/rogue/potions.c @@ -0,0 +1,333 @@ +/* + * Function(s) for dealing with potions + * + * @(#)potions.c 4.46 (Berkeley) 06/07/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" + +typedef struct +{ + int pa_flags; + void (*pa_daemon)(struct rogue_state *rs,int); + int pa_time; + char *pa_high, *pa_straight; +} PACT; + +static PACT p_actions[] = +{ + { ISHUH, unconfuse, HUHDURATION, /* P_CONFUSE */ + "what a tripy feeling!", + "wait, what's going on here. Huh? What? Who?" }, + { ISHALU, come_down, SEEDURATION, /* P_LSD */ + "Oh, wow! Everything seems so cosmic!", + "Oh, wow! Everything seems so cosmic!" }, + { 0, NULL, 0 }, /* P_POISON */ + { 0, NULL, 0 }, /* P_STRENGTH */ + { CANSEE, unsee, SEEDURATION, /* P_SEEINVIS */ + prbuf, + prbuf }, + { 0, NULL, 0 }, /* P_HEALING */ + { 0, NULL, 0 }, /* P_MFIND */ + { 0, NULL, 0 }, /* P_TFIND */ + { 0, NULL, 0 }, /* P_RAISE */ + { 0, NULL, 0 }, /* P_XHEAL */ + { 0, NULL, 0 }, /* P_HASTE */ + { 0, NULL, 0 }, /* P_RESTORE */ + { ISBLIND, sight, SEEDURATION, /* P_BLIND */ + "oh, bummer! Everything is dark! Help!", + "a cloak of darkness falls around you" }, + { ISLEVIT, land, HEALTIME, /* P_LEVIT */ + "oh, wow! You're floating in the air!", + "you start to float in the air" } +}; + +/* + * quaff: + * Quaff a potion from the pack + */ + +void +quaff(struct rogue_state *rs) +{ + THING *obj, *tp, *mp; + bool discardit = FALSE; + bool show, trip; + + obj = get_item(rs,"quaff", POTION); + /* + * Make certain that it is somethings that we want to drink + */ + if (obj == NULL) + return; + if (obj->o_type != POTION) + { + if (!terse) + msg(rs,"yuk! Why would you want to drink that?"); + else + msg(rs,"that's undrinkable"); + return; + } + if (obj == cur_weapon) + cur_weapon = NULL; + /* + * Calculate the effect it has on the poor guy. + */ + trip = on(player, ISHALU); + discardit = (bool)(obj->o_count == 1); + leave_pack(rs,obj, FALSE, FALSE); + switch (obj->o_which) + { + case P_CONFUSE: + do_pot(rs,P_CONFUSE, !trip); + when P_POISON: + pot_info[P_POISON].oi_know = TRUE; + if (ISWEARING(R_SUSTSTR)) + msg(rs,"you feel momentarily sick"); + else + { + chg_str(-(rnd(3) + 1)); + msg(rs,"you feel very sick now"); + come_down(rs,0); + } + when P_HEALING: + pot_info[P_HEALING].oi_know = TRUE; + if ((pstats.s_hpt += roll(pstats.s_lvl, 4)) > max_hp) + pstats.s_hpt = ++max_hp; + sight(rs,0); + msg(rs,"you begin to feel better"); + when P_STRENGTH: + pot_info[P_STRENGTH].oi_know = TRUE; + chg_str(1); + msg(rs,"you feel stronger, now. What bulging muscles!"); + when P_MFIND: + player.t_flags |= SEEMONST; + fuse((void(*)(struct rogue_state *rs,int))turn_see, TRUE, HUHDURATION, AFTER); + if (!turn_see(rs,FALSE)) + msg(rs,"you have a %s feeling for a moment, then it passes", + choose_str("normal", "strange")); + when P_TFIND: + /* + * Potion of magic detection. Show the potions and scrolls + */ + show = FALSE; + if (lvl_obj != NULL) + { + wclear(hw); + for (tp = lvl_obj; tp != NULL; tp = next(tp)) + { + if (is_magic(tp)) + { + show = TRUE; + wmove(hw, tp->o_pos.y, tp->o_pos.x); + waddch(hw, MAGIC); + pot_info[P_TFIND].oi_know = TRUE; + } + } + for (mp = mlist; mp != NULL; mp = next(mp)) + { + for (tp = mp->t_pack; tp != NULL; tp = next(tp)) + { + if (is_magic(tp)) + { + show = TRUE; + wmove(hw, mp->t_pos.y, mp->t_pos.x); + waddch(hw, MAGIC); + } + } + } + } + if (show) + { + pot_info[P_TFIND].oi_know = TRUE; + show_win(rs,"You sense the presence of magic on this level.--More--"); + } + else + msg(rs,"you have a %s feeling for a moment, then it passes", + choose_str("normal", "strange")); + when P_LSD: + if (!trip) + { + if (on(player, SEEMONST)) + turn_see(rs,FALSE); + start_daemon(visuals, 0, BEFORE); + seenstairs = seen_stairs(); + } + do_pot(rs,P_LSD, TRUE); + when P_SEEINVIS: + sprintf(prbuf, "this potion tastes like %s juice", fruit); + show = on(player, CANSEE); + do_pot(rs,P_SEEINVIS, FALSE); + if (!show) + invis_on(); + sight(rs,0); + when P_RAISE: + pot_info[P_RAISE].oi_know = TRUE; + msg(rs,"you suddenly feel much more skillful"); + raise_level(rs); + when P_XHEAL: + pot_info[P_XHEAL].oi_know = TRUE; + if ((pstats.s_hpt += roll(pstats.s_lvl, 8)) > max_hp) + { + if (pstats.s_hpt > max_hp + pstats.s_lvl + 1) + ++max_hp; + pstats.s_hpt = ++max_hp; + } + sight(rs,0); + come_down(rs,0); + msg(rs,"you begin to feel much better"); + when P_HASTE: + pot_info[P_HASTE].oi_know = TRUE; + after = FALSE; + if (add_haste(rs,TRUE)) + msg(rs,"you feel yourself moving much faster"); + when P_RESTORE: + if (ISRING(LEFT, R_ADDSTR)) + add_str(&pstats.s_str, -cur_ring[LEFT]->o_arm); + if (ISRING(RIGHT, R_ADDSTR)) + add_str(&pstats.s_str, -cur_ring[RIGHT]->o_arm); + if (pstats.s_str < max_stats.s_str) + pstats.s_str = max_stats.s_str; + if (ISRING(LEFT, R_ADDSTR)) + add_str(&pstats.s_str, cur_ring[LEFT]->o_arm); + if (ISRING(RIGHT, R_ADDSTR)) + add_str(&pstats.s_str, cur_ring[RIGHT]->o_arm); + msg(rs,"hey, this tastes great. It make you feel warm all over"); + when P_BLIND: + do_pot(rs,P_BLIND, TRUE); + when P_LEVIT: + do_pot(rs,P_LEVIT, TRUE); +#ifdef MASTER + otherwise: + msg(rs,"what an odd tasting potion!"); + return; +#endif + } + status(rs); + /* + * Throw the item away + */ + + call_it(rs,&pot_info[obj->o_which]); + + if (discardit) + discard(obj); + return; +} + +/* + * is_magic: + * Returns true if an object radiates magic + */ +bool +is_magic(THING *obj) +{ + switch (obj->o_type) + { + case ARMOR: + return (bool)((obj->o_flags&ISPROT) || obj->o_arm != a_class[obj->o_which]); + case WEAPON: + return (bool)(obj->o_hplus != 0 || obj->o_dplus != 0); + case POTION: + case SCROLL: + case STICK: + case RING: + case AMULET: + return TRUE; + } + return FALSE; +} + +/* + * invis_on: + * Turn on the ability to see invisible + */ + +void +invis_on() +{ + THING *mp; + + player.t_flags |= CANSEE; + for (mp = mlist; mp != NULL; mp = next(mp)) + if (on(*mp, ISINVIS) && see_monst(mp) && !on(player, ISHALU)) + mvaddch(mp->t_pos.y, mp->t_pos.x, mp->t_disguise); +} + + +/* + * seen_stairs: + * Return TRUE if the player has seen the stairs + */ +bool +seen_stairs() +{ + THING *tp; + + move(stairs.y, stairs.x); + if (inch() == STAIRS) /* it's on the map */ + return TRUE; + if (ce(hero, stairs)) /* It's under him */ + return TRUE; + + /* + * if a monster is on the stairs, this gets hairy + */ + if ((tp = moat(stairs.y, stairs.x)) != NULL) + { + if (see_monst(tp) && on(*tp, ISRUN)) /* if it's visible and awake */ + return TRUE; /* it must have moved there */ + + if (on(player, SEEMONST) /* if she can detect monster */ + && tp->t_oldch == STAIRS) /* and there once were stairs */ + return TRUE; /* it must have moved there */ + } + return FALSE; +} + +/* + * raise_level: + * The guy just magically went up a level. + */ + +void +raise_level(struct rogue_state *rs) +{ + pstats.s_exp = e_levels[pstats.s_lvl-1] + 1L; + check_level(rs); +} + +/* + * do_pot: + * Do a potion with standard setup. This means it uses a fuse and + * turns on a flag + */ + +void +do_pot(struct rogue_state *rs,int type, bool knowit) +{ + PACT *pp; + int t; + + pp = &p_actions[type]; + if (!pot_info[type].oi_know) + pot_info[type].oi_know = knowit; + t = spread(pp->pa_time); + if (!on(player, pp->pa_flags)) + { + player.t_flags |= pp->pa_flags; + fuse(pp->pa_daemon, 0, t, AFTER); + look(rs,FALSE); + } + else + lengthen(pp->pa_daemon, t); + msg(rs,choose_str(pp->pa_high, pp->pa_straight)); +} diff --git a/src/cc/rogue/rings.c b/src/cc/rogue/rings.c new file mode 100644 index 000000000..f9ce50795 --- /dev/null +++ b/src/cc/rogue/rings.c @@ -0,0 +1,206 @@ +/* + * Routines dealing specifically with rings + * + * @(#)rings.c 4.19 (Berkeley) 05/29/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +#include "rogue.h" + +/* + * ring_on: + * Put a ring on a hand + */ + +void +ring_on(struct rogue_state *rs) +{ + THING *obj; + int ring; + + obj = get_item(rs,"put on", RING); + /* + * Make certain that it is somethings that we want to wear + */ + if (obj == NULL) + return; + if (obj->o_type != RING) + { + if (!terse) + msg(rs,"it would be difficult to wrap that around a finger"); + else + msg(rs,"not a ring"); + return; + } + + /* + * find out which hand to put it on + */ + if (is_current(rs,obj)) + return; + + if (cur_ring[LEFT] == NULL && cur_ring[RIGHT] == NULL) + { + if ((ring = gethand(rs)) < 0) + return; + } + else if (cur_ring[LEFT] == NULL) + ring = LEFT; + else if (cur_ring[RIGHT] == NULL) + ring = RIGHT; + else + { + if (!terse) + msg(rs,"you already have a ring on each hand"); + else + msg(rs,"wearing two"); + return; + } + cur_ring[ring] = obj; + + /* + * Calculate the effect it has on the poor guy. + */ + switch (obj->o_which) + { + case R_ADDSTR: + chg_str(obj->o_arm); + break; + case R_SEEINVIS: + invis_on(); + break; + case R_AGGR: + aggravate(rs); + break; + } + + if (!terse) + addmsg(rs,"you are now wearing "); + msg(rs,"%s (%c)", inv_name(obj, TRUE), obj->o_packch); +} + +/* + * ring_off: + * take off a ring + */ + +void +ring_off(struct rogue_state *rs) +{ + int ring; + THING *obj; + + if (cur_ring[LEFT] == NULL && cur_ring[RIGHT] == NULL) + { + if (terse) + msg(rs,"no rings"); + else + msg(rs,"you aren't wearing any rings"); + return; + } + else if (cur_ring[LEFT] == NULL) + ring = RIGHT; + else if (cur_ring[RIGHT] == NULL) + ring = LEFT; + else + if ((ring = gethand(rs)) < 0) + return; + mpos = 0; + obj = cur_ring[ring]; + if (obj == NULL) + { + msg(rs,"not wearing such a ring"); + return; + } + if (dropcheck(rs,obj)) + msg(rs,"was wearing %s(%c)", inv_name(obj, TRUE), obj->o_packch); +} + +/* + * gethand: + * Which hand is the hero interested in? + */ +int +gethand(struct rogue_state *rs) +{ + int c; + + for (;;) + { + if ( rs->replaydone != 0 ) + return(-1); + if (terse) + msg(rs,"left or right ring? "); + else + msg(rs,"left hand or right hand? "); + if ((c = readchar(rs)) == ESCAPE) + return -1; + mpos = 0; + if (c == 'l' || c == 'L') + return LEFT; + else if (c == 'r' || c == 'R') + return RIGHT; + if (terse) + msg(rs,"L or R"); + else + msg(rs,"please type L or R"); + } +} + +/* + * ring_eat: + * How much food does this ring use up? + */ +int +ring_eat(int hand) +{ + THING *ring; + int eat; + static int uses[] = { + 1, /* R_PROTECT */ 1, /* R_ADDSTR */ + 1, /* R_SUSTSTR */ -3, /* R_SEARCH */ + -5, /* R_SEEINVIS */ 0, /* R_NOP */ + 0, /* R_AGGR */ -3, /* R_ADDHIT */ + -3, /* R_ADDDAM */ 2, /* R_REGEN */ + -2, /* R_DIGEST */ 0, /* R_TELEPORT */ + 1, /* R_STEALTH */ 1 /* R_SUSTARM */ + }; + + if ((ring = cur_ring[hand]) == NULL) + return 0; + if ((eat = uses[ring->o_which]) < 0) + eat = (rnd(-eat) == 0); + if (ring->o_which == R_DIGEST) + eat = -eat; + return eat; +} + +/* + * ring_num: + * Print ring bonuses + */ +char * +ring_num(THING *obj) +{ + static char buf[10]; + + if (!(obj->o_flags & ISKNOW)) + return ""; + switch (obj->o_which) + { + case R_PROTECT: + case R_ADDSTR: + case R_ADDDAM: + case R_ADDHIT: + sprintf(buf, " [%s]", num(obj->o_arm, 0, RING)); + otherwise: + return ""; + } + return buf; +} diff --git a/src/cc/rogue/rip.c b/src/cc/rogue/rip.c new file mode 100644 index 000000000..123b1aaa8 --- /dev/null +++ b/src/cc/rogue/rip.c @@ -0,0 +1,461 @@ +/* + * File for the fun ends + * Death or a total win + * + * @(#)rip.c 4.57 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +#include +//#include +//#include +//#include +//#include +#include "rogue.h" +#include "score.h" + +static char *rip[] = { +" __________\n", +" / \\\n", +" / REST \\\n", +" / IN \\\n", +" / PEACE \\\n", +" / \\\n", +" | |\n", +" | |\n", +" | killed by a |\n", +" | |\n", +" | 1980 |\n", +" *| * * * | *\n", +" ________)/\\\\_//(\\/(/\\)/\\//\\/|_)_______\n", + 0 +}; + +/* + * score: + * Figure score and post it. + */ +/* VARARGS2 */ + +void +score(struct rogue_state *rs,int amount, int flags, char monst) +{ + SCORE *scp; + int i; + SCORE *sc2; + SCORE *top_ten, *endp; + if ( rs->guiflag == 0 ) + return; +# ifdef MASTER + int prflags = 0; +# endif + void (*fp)(int); + unsigned int uid; + static char *reason[] = { + "killed", + "quit", + "A total winner", + "killed with Amulet" + }; + + start_score(); + + if (flags >= 0 +#ifdef MASTER + || wizard +#endif + ) + { + mvaddstr(LINES - 1, 0 , "[Press return to continue]"); + refresh(); + wgetnstr(stdscr,prbuf,80); + endwin(); + printf("\n"); + resetltchars(); + /* + * free up space to "guarantee" there is space for the top_ten + */ + delwin(stdscr); + delwin(curscr); + if (hw != NULL) + delwin(hw); + hw = NULL; + } + + top_ten = (SCORE *) malloc(numscores * sizeof (SCORE)); + endp = &top_ten[numscores]; + for (scp = top_ten; scp < endp; scp++) + { + scp->sc_score = 0; + for (i = 0; i < MAXSTR; i++) + scp->sc_name[i] = (unsigned char) rnd(255); + scp->sc_flags = RN; + scp->sc_level = RN; + scp->sc_monster = (unsigned short) RN; + scp->sc_uid = RN; + } + + signal(SIGINT, SIG_DFL); + +#ifdef MASTER + if (wizard) + if (strcmp(prbuf, "names") == 0) + prflags = 1; + else if (strcmp(prbuf, "edit") == 0) + prflags = 2; +#endif + rd_score(top_ten); + /* + * Insert her in list if need be + */ + sc2 = NULL; + if (!noscore) + { + uid = md_getuid(); + for (scp = top_ten; scp < endp; scp++) + if (amount > scp->sc_score) + break; + else if (!allscore && /* only one score per nowin uid */ + flags != 2 && scp->sc_uid == uid && scp->sc_flags != 2) + scp = endp; + if (scp < endp) + { + if (flags != 2 && !allscore) + { + for (sc2 = scp; sc2 < endp; sc2++) + { + if (sc2->sc_uid == uid && sc2->sc_flags != 2) + break; + } + if (sc2 >= endp) + sc2 = endp - 1; + } + else + sc2 = endp - 1; + while (sc2 > scp) + { + *sc2 = sc2[-1]; + sc2--; + } + scp->sc_score = amount; + strncpy(scp->sc_name, whoami, MAXSTR); + scp->sc_flags = flags; + if (flags == 2) + scp->sc_level = max_level; + else + scp->sc_level = level; + scp->sc_monster = monst; + scp->sc_uid = uid; + sc2 = scp; + } + } + /* + * Print the list + */ + if (flags != -1) + putchar('\n'); + printf("Top %s %s:\n", Numname, allscore ? "Scores" : "Rogueists"); + printf(" Score Name\n"); + for (scp = top_ten; scp < endp; scp++) + { + if (scp->sc_score) { + if (sc2 == scp) + md_raw_standout(); + printf("%2d %5d %s: %s on level %d", (int) (scp - top_ten + 1), + scp->sc_score, scp->sc_name, reason[scp->sc_flags], + scp->sc_level); + if (scp->sc_flags == 0 || scp->sc_flags == 3) + printf(" by %s", killname((char) scp->sc_monster, TRUE)); +#ifdef MASTER + if (prflags == 1) + { + printf(" (%s)", md_getrealname(scp->sc_uid)); + } + else if (prflags == 2) + { + fflush(stdout); + if ( fgets(prbuf,10,stdin) != 0 ) + fprintf(stderr,"fgets error\n"); + if (prbuf[0] == 'd') + { + for (sc2 = scp; sc2 < endp - 1; sc2++) + *sc2 = *(sc2 + 1); + sc2 = endp - 1; + sc2->sc_score = 0; + for (i = 0; i < MAXSTR; i++) + sc2->sc_name[i] = (char) rnd(255); + sc2->sc_flags = RN; + sc2->sc_level = RN; + sc2->sc_monster = (unsigned short) RN; + scp--; + } + } + else +#endif /* MASTER */ + printf("."); + if (sc2 == scp) + md_raw_standend(); + putchar('\n'); + } + else + break; + } + /* + * Update the list file + */ + if (sc2 != NULL) + { + if (lock_sc()) + { + fp = signal(SIGINT, SIG_IGN); + wr_score(top_ten); + unlock_sc(); + signal(SIGINT, fp); + } + } + free(top_ten); +} + +/* + * death: + * Do something really fun when he dies + */ + +void +death(struct rogue_state *rs,char monst) +{ + char **dp, *killer; + struct tm *lt; + static time_t date; + //struct tm *localtime(const time_t *); + if ( rs->guiflag == 0 ) + { + //fprintf(stderr,"death during replay by (%c)\n",monst); //sleep(3); + rs->replaydone = (uint32_t)time(NULL); + return; + } + signal(SIGINT, SIG_IGN); + purse -= purse / 10; + signal(SIGINT, leave); + clear(); + killer = killname(monst, FALSE); + if (!tombstone) + { + mvprintw(LINES - 2, 0, "Killed by "); + killer = killname(monst, FALSE); + if (monst != 's' && monst != 'h') + printw("a%s ", vowelstr(killer)); + printw("%s with %d gold", killer, purse); + } + else + { + time(&date); + lt = localtime(&date); + move(8, 0); + dp = rip; + while (*dp) + addstr(*dp++); + mvaddstr(17, center(killer), killer); + if (monst == 's' || monst == 'h') + mvaddch(16, 32, ' '); + else + mvaddstr(16, 33, vowelstr(killer)); + mvaddstr(14, center(whoami), whoami); + sprintf(prbuf, "%d Au", purse); + move(15, center(prbuf)); + addstr(prbuf); + sprintf(prbuf, "%4d", 1900+lt->tm_year); + mvaddstr(18, 26, prbuf); + } + move(LINES - 1, 0); + refresh(); + score(rs,purse, amulet ? 3 : 0, monst); + rogue_bailout(rs); + printf("[Press return to continue]"); + fflush(stdout); + if ( fgets(prbuf,10,stdin) != 0 ) + ; + my_exit(0); +} + +/* + * center: + * Return the index to center the given string + */ +int +center(char *str) +{ + return 28 - (((int)strlen(str) + 1) / 2); +} + +/* + * total_winner: + * Code for a winner + */ + +void +total_winner(struct rogue_state *rs) +{ + THING *obj; + struct obj_info *op; + int worth = 0; + int oldpurse; + + clear(); + standout(); + addstr(" \n"); + addstr(" @ @ @ @ @ @@@ @ @ \n"); + addstr(" @ @ @@ @@ @ @ @ @ \n"); + addstr(" @ @ @@@ @ @ @ @ @ @@@ @@@@ @@@ @ @@@ @ \n"); + addstr(" @@@@ @ @ @ @ @ @ @ @ @ @ @ @ @ @ \n"); + addstr(" @ @ @ @ @ @ @ @@@@ @ @ @@@@@ @ @ @ \n"); + addstr(" @ @ @ @ @ @@ @ @ @ @ @ @ @ @ @ @ \n"); + addstr(" @@@ @@@ @@ @ @ @ @@@@ @@@@ @@@ @@@ @@ @ \n"); + addstr(" \n"); + addstr(" Congratulations, you have made it to the light of day! \n"); + standend(); + addstr("\nYou have joined the elite ranks of those who have escaped the\n"); + addstr("Dungeons of Doom alive. You journey home and sell all your loot at\n"); + addstr("a great profit and are admitted to the Fighters' Guild.\n"); + mvaddstr(LINES - 1, 0, "--Press space to continue--"); + refresh(); + wait_for(rs,' '); + clear(); + mvaddstr(0, 0, " Worth Item\n"); + oldpurse = purse; + for (obj = pack; obj != NULL; obj = next(obj)) + { + switch (obj->o_type) + { + case FOOD: + worth = 2 * obj->o_count; + when WEAPON: + worth = weap_info[obj->o_which].oi_worth; + worth *= 3 * (obj->o_hplus + obj->o_dplus) + obj->o_count; + obj->o_flags |= ISKNOW; + when ARMOR: + worth = arm_info[obj->o_which].oi_worth; + worth += (9 - obj->o_arm) * 100; + worth += (10 * (a_class[obj->o_which] - obj->o_arm)); + obj->o_flags |= ISKNOW; + when SCROLL: + worth = scr_info[obj->o_which].oi_worth; + worth *= obj->o_count; + op = &scr_info[obj->o_which]; + if (!op->oi_know) + worth /= 2; + op->oi_know = TRUE; + when POTION: + worth = pot_info[obj->o_which].oi_worth; + worth *= obj->o_count; + op = &pot_info[obj->o_which]; + if (!op->oi_know) + worth /= 2; + op->oi_know = TRUE; + when RING: + op = &ring_info[obj->o_which]; + worth = op->oi_worth; + if (obj->o_which == R_ADDSTR || obj->o_which == R_ADDDAM || + obj->o_which == R_PROTECT || obj->o_which == R_ADDHIT) + { + if (obj->o_arm > 0) + worth += obj->o_arm * 100; + else + worth = 10; + } + if (!(obj->o_flags & ISKNOW)) + worth /= 2; + obj->o_flags |= ISKNOW; + op->oi_know = TRUE; + when STICK: + op = &ws_info[obj->o_which]; + worth = op->oi_worth; + worth += 20 * obj->o_charges; + if (!(obj->o_flags & ISKNOW)) + worth /= 2; + obj->o_flags |= ISKNOW; + op->oi_know = TRUE; + when AMULET: + worth = 1000; + } + if (worth < 0) + worth = 0; + printw("%c) %5d %s\n", obj->o_packch, worth, inv_name(obj, FALSE)); + purse += worth; + } + printw(" %5d Gold Pieces ", oldpurse); + refresh(); + score(rs,purse, 2, ' '); + my_exit(0); +} + +/* + * killname: + * Convert a code to a monster name + */ +char * +killname(char monst, bool doart) +{ + struct h_list *hp; + char *sp; + bool article; + static struct h_list nlist[] = { + {'a', "arrow", TRUE}, + {'b', "bolt", TRUE}, + {'d', "dart", TRUE}, + {'h', "hypothermia", FALSE}, + {'s', "starvation", FALSE}, + {'\0'} + }; + + if (isupper(monst)) + { + sp = monsters[monst-'A'].m_name; + article = TRUE; + } + else + { + sp = "Wally the Wonder Badger"; + article = FALSE; + for (hp = nlist; hp->h_ch; hp++) + if (hp->h_ch == monst) + { + sp = hp->h_desc; + article = hp->h_print; + break; + } + } + if (doart && article) + sprintf(prbuf, "a%s ", vowelstr(sp)); + else + prbuf[0] = '\0'; + strcat(prbuf, sp); + return prbuf; +} + +/* + * death_monst: + * Return a monster appropriate for a random death. + */ +char +death_monst() +{ + static char poss[] = + { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', + 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'h', 'd', 's', + ' ' /* This is provided to generate the "Wally the Wonder Badger" + message for killer */ + }; + + return poss[rnd(sizeof poss / sizeof (char))]; +} diff --git a/src/cc/rogue/rogue.6 b/src/cc/rogue/rogue.6 new file mode 100644 index 000000000..6b63252ab --- /dev/null +++ b/src/cc/rogue/rogue.6 @@ -0,0 +1,96 @@ +.\" +.\" @(#)rogue.6 6.2 (Berkeley) 5/6/86 +.\" +.\" Rogue: Exploring the Dungeons of Doom +.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman +.\" All rights reserved. +.\" +.\" See the file LICENSE.TXT for full copyright and licensing information. +.\" +.TH ROGUE 6 "May 6, 1986" +.UC 4 +.SH NAME +rogue \- Exploring The Dungeons of Doom +.SH SYNOPSIS +.B rogue +[ +.B \-r +] +[ +.I save_file +] +[ +.B \-s +] +[ +.B \-d +] +.SH DESCRIPTION +.PP +.I Rogue +is a computer fantasy game with a new twist. It is crt oriented and the +object of the game is to survive the attacks of various monsters and get +a lot of gold, rather than the puzzle solving orientation of most computer +fantasy games. +.PP +To get started you really only need to know two commands. The command +.B ? +will give you a list of the available commands and the command +.B / +will identify the things you see on the screen. +.PP +To win the game (as opposed to merely playing to beat other people's high +scores) you must locate the Amulet of Yendor which is somewhere below +the 20th level of the dungeon and get it out. Nobody has achieved this +yet and if somebody does, they will probably go down in history as a hero +among heroes. +.PP +When the game ends, either by your death, when you quit, or if you (by +some miracle) manage to win, +.I rogue +will give you a list of the top-ten scorers. The scoring is based entirely +upon how much gold you get. There is a 10% penalty for getting yourself +killed. +.PP +If +.I save_file +is specified, +rogue will be restored from the specified saved game file. +If the +.B \-r +option is used, the save game file is presumed to be the default. +.PP +The +.B \-s +option will print out the list of scores. +.PP +The +.B \-d +option will kill you and try to add you to the score file. +.PP +For more detailed directions, read the document +.I "A Guide to the Dungeons of Doom." +.SH AUTHORS +Michael C. Toy, +Kenneth C. R. C. Arnold, +Glenn Wichman +.SH FILES +.DT +.ta \w'rogue.scr\ \ \ 'u +rogue.scr Score file +.br +\fB~\fP/rogue.save Default save file +.SH SEE ALSO +Michael C. Toy +and +Kenneth C. R. C. Arnold, +.I "A guide to the Dungeons of Doom" +.SH BUGS +.PP +Probably infinite +(although countably infinite). +However, +that Ice Monsters sometimes transfix you permanently is +.I not +a bug. +It's a feature. diff --git a/src/cc/rogue/rogue.6.in b/src/cc/rogue/rogue.6.in new file mode 100644 index 000000000..a9d6d9903 --- /dev/null +++ b/src/cc/rogue/rogue.6.in @@ -0,0 +1,96 @@ +.\" +.\" @(#)rogue.6 6.2 (Berkeley) 5/6/86 +.\" +.\" Rogue: Exploring the Dungeons of Doom +.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman +.\" All rights reserved. +.\" +.\" See the file LICENSE.TXT for full copyright and licensing information. +.\" +.TH ROGUE 6 "May 6, 1986" +.UC 4 +.SH NAME +rogue \- Exploring The Dungeons of Doom +.SH SYNOPSIS +.B @PROGRAM@ +[ +.B \-r +] +[ +.I save_file +] +[ +.B \-s +] +[ +.B \-d +] +.SH DESCRIPTION +.PP +.I Rogue +is a computer fantasy game with a new twist. It is crt oriented and the +object of the game is to survive the attacks of various monsters and get +a lot of gold, rather than the puzzle solving orientation of most computer +fantasy games. +.PP +To get started you really only need to know two commands. The command +.B ? +will give you a list of the available commands and the command +.B / +will identify the things you see on the screen. +.PP +To win the game (as opposed to merely playing to beat other people's high +scores) you must locate the Amulet of Yendor which is somewhere below +the 20th level of the dungeon and get it out. Nobody has achieved this +yet and if somebody does, they will probably go down in history as a hero +among heroes. +.PP +When the game ends, either by your death, when you quit, or if you (by +some miracle) manage to win, +.I rogue +will give you a list of the top-ten scorers. The scoring is based entirely +upon how much gold you get. There is a 10% penalty for getting yourself +killed. +.PP +If +.I save_file +is specified, +rogue will be restored from the specified saved game file. +If the +.B \-r +option is used, the save game file is presumed to be the default. +.PP +The +.B \-s +option will print out the list of scores. +.PP +The +.B \-d +option will kill you and try to add you to the score file. +.PP +For more detailed directions, read the document +.I "A Guide to the Dungeons of Doom." +.SH AUTHORS +Michael C. Toy, +Kenneth C. R. C. Arnold, +Glenn Wichman +.SH FILES +.DT +.ta \w'@SCOREFILE@\ \ \ 'u +@SCOREFILE@ Score file +.br +\fB~\fP/rogue.save Default save file +.SH SEE ALSO +Michael C. Toy +and +Kenneth C. R. C. Arnold, +.I "A guide to the Dungeons of Doom" +.SH BUGS +.PP +Probably infinite +(although countably infinite). +However, +that Ice Monsters sometimes transfix you permanently is +.I not +a bug. +It's a feature. diff --git a/src/cc/rogue/rogue.777.0 b/src/cc/rogue/rogue.777.0 new file mode 100644 index 000000000..e56f7b148 --- /dev/null +++ b/src/cc/rogue/rogue.777.0 @@ -0,0 +1 @@ +llllljhhl jllllllllllllllllllkkllllklllljllljjllllllllllll \ No newline at end of file diff --git a/src/cc/rogue/rogue.777.1 b/src/cc/rogue/rogue.777.1 new file mode 100644 index 000000000..68b8ab099 --- /dev/null +++ b/src/cc/rogue/rogue.777.1 @@ -0,0 +1 @@ +hhhhhhhhhhkkhhhhhhhhhhhhhjjhhhhhhhhjjjjjjjjjlllllllllllllk \ No newline at end of file diff --git a/src/cc/rogue/rogue.777.2 b/src/cc/rogue/rogue.777.2 new file mode 100644 index 000000000..93a664d1e --- /dev/null +++ b/src/cc/rogue/rogue.777.2 @@ -0,0 +1 @@ +lllljjllllll l ll lll l lll l ll l ljjj j j jhhhhhjjllllll \ No newline at end of file diff --git a/src/cc/rogue/rogue.777.3 b/src/cc/rogue/rogue.777.3 new file mode 100644 index 000000000..e69de29bb diff --git a/src/cc/rogue/rogue.c b/src/cc/rogue/rogue.c new file mode 100644 index 000000000..c687fd019 --- /dev/null +++ b/src/cc/rogue/rogue.c @@ -0,0 +1,773 @@ +/* + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + * + * @(#)main.c 4.22 (Berkeley) 02/05/99 + */ + +//#include +//#include +#include +//#include +//#include + +#include "rogue.h" +#ifdef STANDALONE +#include "../komodo/src/komodo_cJSON.h" +#else +#include "../../komodo_cJSON.h" +#endif + +/* + * main: + * The main program, of course + */ +struct rogue_state globalR; +char Gametxidstr[67]; +void garbage_collect(); + +void purge_obj_guess(struct obj_info *array,int32_t n) +{ + int32_t i; + for (i=0; iseed; + //clear(); + purge_obj_guess(things,NUMTHINGS); + purge_obj_guess(ring_info,MAXRINGS); + purge_obj_guess(pot_info,MAXPOTIONS); + purge_obj_guess(arm_info,MAXARMORS); + purge_obj_guess(scr_info,MAXSCROLLS); + purge_obj_guess(weap_info,MAXWEAPONS + 1); + purge_obj_guess(ws_info,MAXSTICKS); + free_list(player._t._t_pack); + for (tp = mlist; tp != NULL; tp = next(tp)) + free_list(tp->t_pack); + free_list(mlist); + free_list(lvl_obj); + garbage_collect(); + + externs_clear(); + memset(d_list,0,sizeof(d_list)); + + memcpy(passages,origpassages,sizeof(passages)); + memcpy(monsters,origmonsters,sizeof(monsters)); + memcpy(things,origthings,sizeof(things)); + + memcpy(ring_info,origring_info,sizeof(ring_info)); + memcpy(pot_info,origpot_info,sizeof(pot_info)); + memcpy(arm_info,origarm_info,sizeof(arm_info)); + memcpy(scr_info,origscr_info,sizeof(scr_info)); + memcpy(weap_info,origweap_info,sizeof(weap_info)); + memcpy(ws_info,origws_info,sizeof(ws_info)); + + initscr(); /* Start up cursor package */ + init_probs(); /* Set up prob tables for objects */ + init_player(rs); /* Set up initial player stats */ + init_names(); /* Set up names of scrolls */ + init_colors(); /* Set up colors of potions */ + init_stones(); /* Set up stone settings of rings */ + init_materials(); /* Set up materials of wands */ + setup(); + + /* + * The screen must be at least NUMLINES x NUMCOLS + */ + if (LINES < NUMLINES || COLS < NUMCOLS) + { + printf("\nSorry, the screen must be at least %dx%d\n", NUMLINES, NUMCOLS); + endwin(); + my_exit(1); + } + //fprintf(stderr,"LINES %d, COLS %d\n",LINES,COLS); + + // Set up windows + if ( hw == NULL ) + { + hw = newwin(LINES, COLS, 0, 0); + } + idlok(stdscr, TRUE); + idlok(hw, TRUE); +#ifdef MASTER + noscore = wizard; +#endif + new_level(rs); // Draw current level + // Start up daemons and fuses + start_daemon(runners, 0, AFTER); + start_daemon(doctor, 0, AFTER); + fuse(swander, 0, WANDERTIME, AFTER); + start_daemon(stomach, 0, AFTER); + if ( rs->restoring != 0 ) + { + restore_player(rs); + } + playit(rs); +} + +int32_t roguefname(char *fname,uint64_t seed,int32_t counter) +{ + sprintf(fname,"rogue.%llu.%d",(long long)seed,counter); + return(0); +} + +int32_t flushkeystrokes_local(struct rogue_state *rs,int32_t waitflag) +{ +#ifndef BUILD_ROGUE + char fname[1024]; FILE *fp; int32_t i,retflag = -1; + rs->counter++; + roguefname(fname,rs->seed,rs->counter); + if ( (fp= fopen(fname,"wb")) != 0 ) + { + if ( fwrite(rs->buffered,1,rs->num,fp) == rs->num ) + { + rs->num = 0; + retflag = 0; + fclose(fp); + /*if ( (fp= fopen("savefile","wb")) != 0 ) + { + save_file(rs,fp,0); + if ( 0 && (fp= fopen("savefile","rb")) != 0 ) + { + for (i=0; i<0x150; i++) + fprintf(stderr,"%02x",fgetc(fp)); + fprintf(stderr," first part rnd.%d\n",rnd(1000)); + fclose(fp); + }*/ + roguefname(fname,rs->seed,rs->counter+1); + if ( (fp= fopen(fname,"wb")) != 0 ) // truncate next file + fclose(fp); + //fprintf(stderr,"savefile <- %s retflag.%d\n",fname,retflag); + //} + } else fprintf(stderr,"error writing (%s)\n",fname); + } else fprintf(stderr,"error creating (%s)\n",fname); + return(retflag); +#else + return(0); +#endif +} + +#ifdef BUILD_ROGUE +// stubs for inside daemon + +int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num) +{ +} + +int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr) +{ + return(-1); +} +#endif + +int32_t flushkeystrokes(struct rogue_state *rs,int32_t waitflag) +{ + if ( rs->num > 0 ) + { + if ( rogue_progress(rs,waitflag,rs->seed,rs->buffered,rs->num) > 0 ) + { + flushkeystrokes_local(rs,waitflag); + memset(rs->buffered,0,sizeof(rs->buffered)); + } + } + return(0); +} + +void rogue_bailout(struct rogue_state *rs) +{ + char cmd[512]; + flushkeystrokes(rs,1); + //sleep(5); + return; + /*fprintf(stderr,"bailing out\n"); + sprintf(cmd,"./komodo-cli -ac_name=ROGUE cclib bailout 17 \\\"[%%22%s%%22]\\\" >> bailout.log",Gametxidstr); + if ( system(cmd) != 0 ) + fprintf(stderr,"error issuing (%s)\n",cmd);*/ +} + +#ifdef _WIN32 +#ifdef _MSC_VER +#define sleep(x) Sleep(1000*(x)) +#endif +#endif + +int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis) +{ + struct rogue_state *rs; FILE *fp; int32_t i,n; + rs = (struct rogue_state *)calloc(1,sizeof(*rs)); + rs->seed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = sleepmillis * 1000; + if ( player != 0 ) + { + rs->P = *player; + rs->restoring = 1; + //fprintf(stderr,"restore player packsize.%d HP.%d\n",rs->P.packsize,rs->P.hitpoints); + if ( rs->P.packsize > MAXPACK ) + rs->P.packsize = MAXPACK; + } + globalR = *rs; + uint32_t starttime = (uint32_t)time(NULL); + rogueiterate(rs); + + /* + // keypress after replay + printf("[Press return to continue]"); + fflush(stdout); + if (fgets(prbuf, 10, stdin) != 0); + */ + + if ( 0 ) + { + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL) - starttime); + sleep(2); + + starttime = (uint32_t)time(NULL); + for (i=0; i<10000; i++) + { + memset(rs,0,sizeof(*rs)); + rs->seed = seed; + rs->keystrokes = keystrokes; + rs->numkeys = num; + rs->sleeptime = 0; + rogueiterate(rs); + } + fprintf(stderr,"elapsed %d seconds\n",(uint32_t)time(NULL)-starttime); + sleep(3); + } + if ( (fp= fopen("checkfile","wb")) != 0 ) + { + save_file(rs,fp,0); + //fprintf(stderr,"gold.%d hp.%d strength.%d/%d level.%d exp.%d dungeon.%d data[%d]\n",rs->P.gold,rs->P.hitpoints,rs->P.strength&0xffff,rs->P.strength>>16,rs->P.level,rs->P.experience,rs->P.dungeonlevel,rs->playersize); + if ( newdata != 0 && rs->playersize > 0 ) + memcpy(newdata,rs->playerdata,rs->playersize); + } + n = rs->playersize; + free(rs); + return(n); +} + +long get_filesize(FILE *fp) +{ + long fsize,fpos = ftell(fp); + fseek(fp,0,SEEK_END); + fsize = ftell(fp); + fseek(fp,fpos,SEEK_SET); + return(fsize); +} + +char *rogue_keystrokesload(int32_t *numkeysp,uint64_t seed,int32_t counter) +{ + char fname[1024],*keystrokes = 0; FILE *fp; long fsize; int32_t num = 0; + *numkeysp = 0; + while ( 1 ) + { + roguefname(fname,seed,counter); + //printf("check (%s)\n",fname); + if ( (fp= fopen(fname,"rb")) == 0 ) + break; + if ( (fsize= get_filesize(fp)) <= 0 ) + { + fclose(fp); + //printf("fsize.%ld\n",fsize); + break; + } + if ( (keystrokes= (char *)realloc(keystrokes,num+fsize)) == 0 ) + { + fprintf(stderr,"error reallocating keystrokes\n"); + fclose(fp); + return(0); + } + if ( fread(&keystrokes[num],1,fsize,fp) != fsize ) + { + fprintf(stderr,"error reading keystrokes from (%s)\n",fname); + fclose(fp); + free(keystrokes); + return(0); + } + fclose(fp); + num += fsize; + counter++; + //fprintf(stderr,"loaded %ld from (%s) total %d\n",fsize,fname,num); + } + *numkeysp = num; + return(keystrokes); +} + +int32_t rogue_replay(uint64_t seed,int32_t sleeptime) +{ + FILE *fp; char fname[1024]; char *keystrokes = 0; long fsize; int32_t i,num=0,counter = 0; struct rogue_state *rs; struct rogue_player P,*player = 0; + if ( seed == 0 ) + seed = 777; + keystrokes = rogue_keystrokesload(&num,seed,counter); + if ( num > 0 ) + { + sprintf(fname,"rogue.%llu.player",(long long)seed); + if ( (fp=fopen(fname,"rb")) != 0 ) + { + if ( fread(&P,1,sizeof(P),fp) > 0 ) + { + //printf("max size player\n"); + player = &P; + } + fclose(fp); + } + rogue_replay2(0,seed,keystrokes,num,player,sleeptime); + mvaddstr(LINES - 2, 0, (char *)"replay completed"); + endwin(); + my_exit(0); + } + if ( keystrokes != 0 ) + free(keystrokes); + return(num); +} + +int rogue(int argc, char **argv, char **envp) +{ + char *env; int lowtime; struct rogue_state *rs = &globalR; + memset(rs,0,sizeof(*rs)); + rs->guiflag = 1; + rs->sleeptime = 1; // non-zero to allow refresh() + if ( argc == 3 && strlen(argv[2]) == 64 ) + { + #ifdef _WIN32 + #ifdef _MSC_VER + rs->seed = _strtoui64(argv[1], NULL, 10); + #else + rs->seed = atol(argv[1]); // windows, but not MSVC + #endif // _MSC_VER + #else + rs->seed = atol(argv[1]); // non-windows + #endif // _WIN32 + strcpy(Gametxidstr,argv[2]); + fprintf(stderr,"setplayerdata\n"); + if ( rogue_setplayerdata(rs,Gametxidstr) < 0 ) + { + fprintf(stderr,"invalid gametxid, or already started\n"); + return(-1); + } + } else rs->seed = 777; + md_init(); + +#ifdef MASTER + /* + * Check to see if he is a wizard + */ + if (argc >= 2 && argv[1][0] == '\0') + if (strcmp(PASSWD, md_crypt(md_getpass("wizard's password: "), "mT")) == 0) + { + wizard = TRUE; + player.t_flags |= SEEMONST; + argv++; + argc--; + } + +#endif + + /* + * get home and options from environment + */ + + strncpy(home, md_gethomedir(), MAXSTR); + + strcpy(file_name, home); + strcat(file_name, "rogue.save"); + + if ((env = getenv("ROGUEOPTS")) != NULL) + parse_opts(env); + //if (env == NULL || whoami[0] == '\0') + // strucpy(whoami, md_getusername(), (int) strlen(md_getusername())); + lowtime = (int) time(NULL); +#ifdef MASTER + if (wizard && getenv("SEED") != NULL) + rs->seed = atoi(getenv("SEED")); + else +#endif + //dnum = lowtime + md_getpid(); + if ( rs != 0 ) + seed = rs->seed; + else seed = 777; + //dnum = (int)seed; + + open_score(); + + /* + * Drop setuid/setgid after opening the scoreboard file. + */ + + md_normaluser(); + + /* + * check for print-score option + */ + + md_normaluser(); /* we drop any setgid/setuid priveldges here */ + + if (argc == 2) + { + if (strcmp(argv[1], "-s") == 0) + { + noscore = TRUE; + score(rs,0, -1, 0); + exit(0); + } + else if (strcmp(argv[1], "-d") == 0) + { + rs->seed = rnd(100); /* throw away some rnd()s to break patterns */ + while (--rs->seed) + rnd(100); + purse = rnd(100) + 1; + level = rnd(100) + 1; + initscr(); + getltchars(); + death(rs,death_monst()); + exit(0); + } + } + + init_check(); /* check for legal startup */ + if (argc == 2) + if (!restore(rs,argv[1], envp)) /* Note: restore will never return */ + my_exit(1); +#ifdef MASTER + if (wizard) + printf("Hello %s, welcome to dungeon #%d", whoami, dnum); + else +#endif + printf("Hello %s, just a moment while I dig the dungeon... seed.%llu", whoami,(long long)rs->seed); + fflush(stdout); + fprintf(stderr,"rogueiterate\n"); + rogueiterate(rs); + return(0); +} + +/* + * endit: + * Exit the program abnormally. + */ + +void +endit(int sig) +{ + NOOP(sig); + fatal("Okay, bye bye!\n"); +} + +/* + * fatal: + * Exit the program, printing a message. + */ + +void +fatal(char *s) +{ + mvaddstr(LINES - 2, 0, s); + refresh(); + endwin(); + my_exit(0); +} + +/* + * rnd: + * Pick a very random number. + */ +int +rnd(int range) +{ + return range == 0 ? 0 : abs((int) RN) % range; +} + +/* + * roll: + * Roll a number of dice + */ +int +roll(int number, int sides) +{ + int dtotal = 0; + + while (number--) + dtotal += rnd(sides)+1; + return dtotal; +} + +/* + * tstp: + * Handle stop and start signals + */ + +void +tstp(int ignored) +{ + int y, x; + int oy, ox; + + NOOP(ignored); + + /* + * leave nicely + */ + getyx(curscr, oy, ox); + mvcur(0, COLS - 1, LINES - 1, 0); + endwin(); + resetltchars(); + fflush(stdout); + md_tstpsignal(); + + /* + * start back up again + */ + md_tstpresume(); + raw(); + noecho(); + keypad(stdscr,1); + playltchars(); + clearok(curscr, TRUE); + wrefresh(curscr); + getyx(curscr, y, x); + mvcur(y, x, oy, ox); + fflush(stdout); + wmove(curscr,oy,ox); +/*#ifndef __APPLE__ +#ifndef BUILD_ROGUE + curscr->_cury = oy; + curscr->_curx = ox; +#endif +#endif*/ +} + + +#ifdef _WIN32 +#ifdef _MSC_VER +void usleep(int32_t micros) +{ + if (micros < 1000) + Sleep(1); + else Sleep(micros / 1000); +} +#endif +#endif + +/* + * playit: + * The main loop of the program. Loop until the game is over, + * refreshing things and looking at the proper times. + */ + +void +playit(struct rogue_state *rs) +{ + char *opts; + + /* + * set up defaults for slow terminals + */ + + if (baudrate() <= 1200) + { + terse = TRUE; + jump = TRUE; + see_floor = FALSE; + } + + if (md_hasclreol()) + inv_type = INV_CLEAR; + + /* + * parse environment declaration of options + */ + if ((opts = getenv("ROGUEOPTS")) != NULL) + parse_opts(opts); + + + oldpos = hero; + oldrp = roomin(rs,&hero); + while (playing) + { + command(rs); // Command execution + if ( rs->guiflag == 0 ) + { + if ( rs->replaydone != 0 ) + { + if ( 0 && rs->sleeptime != 0 ) + sleep(3); + return; + } + if ( rs->sleeptime != 0 ) + usleep(rs->sleeptime); + } + else + { + if ( rs->needflush != 0 ) + { + if ( flushkeystrokes(rs,0) == 0 ) + rs->needflush = 0; + } + } + } + if ( rs->guiflag != 0 ) + flushkeystrokes(rs,1); + endit(0); +} + + + +int32_t _quit() +{ + struct rogue_state *rs = &globalR; + int oy, ox, c; + //fprintf(stderr,"inside quit(%d)\n",sig); + getyx(curscr, oy, ox); + msg(rs,"really quit?"); + //sleep(1); + if ( (c= readchar(rs)) == 'y') + { + if ( rs->guiflag != 0 ) + { + signal(SIGINT, leave); + clear(); + mvprintw(LINES - 2, 0, "You quit with %d gold pieces", purse); + move(LINES - 1, 0); + if ( rs->sleeptime != 0 ) + refresh(); + score(rs,purse, 1, 0); + flushkeystrokes(rs,1); + my_exit(0); + } + else + { + //score(rs,purse, 1, 0); + //fprintf(stderr,"done! (%c)\n",c); + } + return(1); + } + else + { + //fprintf(stderr,"'Q' answer (%c)\n",c); + move(0, 0); + clrtoeol(); + status(rs); + move(oy, ox); + if ( rs->sleeptime != 0 ) + refresh(); + mpos = 0; + count = 0; + to_death = FALSE; + return(0); + } +} + +/* + * quit: + * Have player make certain, then exit. + */ + +void quit(int sig) +{ + struct rogue_state *rs = &globalR; + int oy, ox, c; + //fprintf(stderr,"inside quit(%d)\n",sig); + if ( rs->guiflag != 0 ) + { + NOOP(sig); + + /* + * Reset the signal in case we got here via an interrupt + */ + if (!q_comm) + mpos = 0; + } + _quit(); +} + +/* + * leave: + * Leave quickly, but curteously + */ + +void +leave(int sig) +{ + static char buf[BUFSIZ]; + + NOOP(sig); + + setbuf(stdout, buf); /* throw away pending output */ + + if (!isendwin()) + { + mvcur(0, COLS - 1, LINES - 1, 0); + endwin(); + } + + putchar('\n'); + my_exit(0); +} + +/* + * shell: + * Let them escape for a while + */ + +void +shell(struct rogue_state *rs) +{ + if ( rs != 0 && rs->guiflag != 0 ) + { + /* + * Set the terminal back to original mode + */ + move(LINES-1, 0); + refresh(); + endwin(); + resetltchars(); + putchar('\n'); + in_shell = TRUE; + after = FALSE; + fflush(stdout); + /* + * Fork and do a shell + */ + md_shellescape(); + + printf("\n[Press return to continue]"); + fflush(stdout); + noecho(); + raw(); + keypad(stdscr,1); + playltchars(); + in_shell = FALSE; + wait_for(rs,'\n'); + clearok(stdscr, TRUE); + } + else fprintf(stderr,"no shell in the blockchain\n"); +} + +/* + * my_exit: + * Leave the process properly + */ + +void +my_exit(int st) +{ + uint32_t counter; + resetltchars(); + if ( globalR.guiflag != 0 || globalR.sleeptime != 0 ) + exit(st); + else if ( counter++ < 10 ) + { + fprintf(stderr,"would have exit.(%d) sleeptime.%d\n",st,globalR.sleeptime); + globalR.replaydone = 1; + } +} + diff --git a/src/cc/rogue/rogue.cat b/src/cc/rogue/rogue.cat new file mode 100644 index 000000000..66d9605a2 --- /dev/null +++ b/src/cc/rogue/rogue.cat @@ -0,0 +1,61 @@ +ROGUE(6) ROGUE(6) + + + +NAME + rogue - Exploring The Dungeons of Doom + +SYNOPSIS + rogue [ -r ] [ save_file ] [ -s ] [ -d ] + +DESCRIPTION + Rogue is a computer fantasy game with a new twist. It is crt oriented + and the object of the game is to survive the attacks of various mon- + sters and get a lot of gold, rather than the puzzle solving orientation + of most computer fantasy games. + + To get started you really only need to know two commands. The command + ? will give you a list of the available commands and the command / + will identify the things you see on the screen. + + To win the game (as opposed to merely playing to beat other people's + high scores) you must locate the Amulet of Yendor which is somewhere + below the 20th level of the dungeon and get it out. Nobody has + achieved this yet and if somebody does, they will probably go down in + history as a hero among heroes. + + When the game ends, either by your death, when you quit, or if you (by + some miracle) manage to win, rogue will give you a list of the top-ten + scorers. The scoring is based entirely upon how much gold you get. + There is a 10% penalty for getting yourself killed. + + If save_file is specified, rogue will be restored from the specified + saved game file. If the -r option is used, the save game file is pre- + sumed to be the default. + + The -s option will print out the list of scores. + + The -d option will kill you and try to add you to the score file. + + For more detailed directions, read the document A Guide to the Dungeons + of Doom. + +AUTHORS + Michael C. Toy, Kenneth C. R. C. Arnold, Glenn Wichman + +FILES + rogue.scr Score file + ~/rogue.save Default save file + +SEE ALSO + Michael C. Toy and Kenneth C. R. C. Arnold, A guide to the Dungeons of + Doom + +BUGS + Probably infinite (although countably infinite). However, that Ice + Monsters sometimes transfix you permanently is not a bug. It's a fea- + ture. + + + +4th Berkeley Distribution May 6, 1986 ROGUE(6) diff --git a/src/cc/rogue/rogue.cat.in b/src/cc/rogue/rogue.cat.in new file mode 100644 index 000000000..8dbef73c1 --- /dev/null +++ b/src/cc/rogue/rogue.cat.in @@ -0,0 +1,61 @@ +ROGUE(6) ROGUE(6) + + + +NAME + rogue - Exploring The Dungeons of Doom + +SYNOPSIS + @PROGRAM@ [ -r ] [ save_file ] [ -s ] [ -d ] + +DESCRIPTION + Rogue is a computer fantasy game with a new twist. It is crt oriented + and the object of the game is to survive the attacks of various mon- + sters and get a lot of gold, rather than the puzzle solving orientation + of most computer fantasy games. + + To get started you really only need to know two commands. The command + ? will give you a list of the available commands and the command / + will identify the things you see on the screen. + + To win the game (as opposed to merely playing to beat other people's + high scores) you must locate the Amulet of Yendor which is somewhere + below the 20th level of the dungeon and get it out. Nobody has + achieved this yet and if somebody does, they will probably go down in + history as a hero among heroes. + + When the game ends, either by your death, when you quit, or if you (by + some miracle) manage to win, rogue will give you a list of the top-ten + scorers. The scoring is based entirely upon how much gold you get. + There is a 10% penalty for getting yourself killed. + + If save_file is specified, rogue will be restored from the specified + saved game file. If the -r option is used, the save game file is pre- + sumed to be the default. + + The -s option will print out the list of scores. + + The -d option will kill you and try to add you to the score file. + + For more detailed directions, read the document A Guide to the Dungeons + of Doom. + +AUTHORS + Michael C. Toy, Kenneth C. R. C. Arnold, Glenn Wichman + +FILES + @SCOREFILE@ Score file + ~/rogue.save Default save file + +SEE ALSO + Michael C. Toy and Kenneth C. R. C. Arnold, A guide to the Dungeons of + Doom + +BUGS + Probably infinite (although countably infinite). However, that Ice + Monsters sometimes transfix you permanently is not a bug. It's a fea- + ture. + + + +4th Berkeley Distribution May 6, 1986 ROGUE(6) diff --git a/src/cc/rogue/rogue.desktop b/src/cc/rogue/rogue.desktop new file mode 100644 index 000000000..74f9518c7 --- /dev/null +++ b/src/cc/rogue/rogue.desktop @@ -0,0 +1,11 @@ +[Desktop Entry] +Encoding=UTF-8 +Name=Rogue +GenericName=Rogue +Comment=The original curses-based adventure game +Exec=rogue +Icon=rogue.png +Terminal=true +Type=Application +Categories=Game;RolePlaying; +Version=1.0 diff --git a/src/cc/rogue/rogue.doc b/src/cc/rogue/rogue.doc new file mode 100644 index 000000000..684738d11 --- /dev/null +++ b/src/cc/rogue/rogue.doc @@ -0,0 +1,858 @@ + + + + + + + + A Guide to the Dungeons of Doom + + + Michael C. Toy + Kenneth C. R. C. Arnold + + + Computer Systems Research Group + Department of Electrical Engineering and Computer Science + University of California + Berkeley, California 94720 + + + + + ABSTRACT + + Rogue is a visual CRT based fantasy game which runs + under the UNIX timesharing system. This paper de- + scribes how to play rogue, and gives a few hints for + those who might otherwise get lost in the Dungeons + of Doom. + + + + +1. Introduction + + You have just finished your years as a student at the +local fighter's guild. After much practice and sweat you +have finally completed your training and are ready to embark +upon a perilous adventure. As a test of your skills, the +local guildmasters have sent you into the Dungeons of Doom. +Your task is to return with the Amulet of Yendor. Your +reward for the completion of this task will be a full mem- +bership in the local guild. In addition, you are allowed to +keep all the loot you bring back from the dungeons. + + In preparation for your journey, you are given an +enchanted mace, a bow, and a quiver of arrows taken from a +dragon's hoard in the far off Dark Mountains. You are also +outfitted with elf-crafted armor and given enough food to +reach the dungeons. You say goodbye to family and friends +for what may be the last time and head up the road. + + You set out on your way to the dungeons and after sev- +eral days of uneventful travel, you see the ancient ruins +that mark the entrance to the Dungeons of Doom. It is late +at night, so you make camp at the entrance and spend the +____________________ + UNIX is a trademark of Bell Laboratories + + + + + + + + + + + + +USD:33-2 A Guide to the Dungeons of Doom + + +night sleeping under the open skies. In the morning you +gather your weapons, put on your armor, eat what is almost +your last food, and enter the dungeons. + +2. What is going on here? + + You have just begun a game of rogue. Your goal is to +grab as much treasure as you can, find the Amulet of Yendor, +and get out of the Dungeons of Doom alive. On the screen, a +map of where you have been and what you have seen on the +current dungeon level is kept. As you explore more of the +level, it appears on the screen in front of you. + + Rogue differs from most computer fantasy games in that +it is screen oriented. Commands are all one or two +keystrokes1 and the results of your commands are displayed +graphically on the screen rather than being explained in +words.2 + + Another major difference between rogue and other com- +puter fantasy games is that once you have solved all the +puzzles in a standard fantasy game, it has lost most of its +excitement and it ceases to be fun. Rogue, on the other +hand, generates a new dungeon every time you play it and +even the author finds it an entertaining and exciting game. + +3. What do all those things on the screen mean? + + In order to understand what is going on in rogue you +have to first get some grasp of what rogue is doing with the +screen. The rogue screen is intended to replace the "You +can see ..." descriptions of standard fantasy games. Figure +1 is a sample of what a rogue screen might look like. + +3.1. The bottom line + + At the bottom line of the screen are a few pieces of +cryptic information describing your current status. Here is +an explanation of what these things mean: + +Level This number indicates how deep you have gone in the + dungeon. It starts at one and goes up as you go + deeper into the dungeon. + +Gold The number of gold pieces you have managed to find + and keep with you so far. +____________________ + 1 As opposed to pseudo English sentences. + 2 A minimum screen size of 24 lines by 80 columns is re- +quired. If the screen is larger, only the 24x80 section +will be used for the map. + + + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-3 + + + +____________________________________________________________ + + + ------------ + |..........+ + |..@....]..| + |....B.....| + |..........| + -----+------ + + + +Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0 + + Figure 1 +____________________________________________________________ + + +Hp Your current and maximum health points. Health + points indicate how much damage you can take before + you die. The more you get hit in a fight, the lower + they get. You can regain health points by resting. + The number in parentheses is the maximum number your + health points can reach. + +Str Your current strength and maximum ever strength. + This can be any integer less than or equal to 31, or + greater than or equal to three. The higher the num- + ber, the stronger you are. The number in the paren- + theses is the maximum strength you have attained so + far this game. + +Arm Your current armor protection. This number indicates + how effective your armor is in stopping blows from + unfriendly creatures. The higher this number is, the + more effective the armor. + +Exp These two numbers give your current experience level + and experience points. As you do things, you gain + experience points. At certain experience point + totals, you gain an experience level. The more expe- + rienced you are, the better you are able to fight and + to withstand magical attacks. + +3.2. The top line + + The top line of the screen is reserved for printing +messages that describe things that are impossible to repre- +sent visually. If you see a "--More--" on the top line, +this means that rogue wants to print another message on the +screen, but it wants to make certain that you have read the +one that is there first. To read the next message, just + + + + + + + + + + +USD:33-4 A Guide to the Dungeons of Doom + + +type a space. + +3.3. The rest of the screen + + The rest of the screen is the map of the level as you +have explored it so far. Each symbol on the screen repre- +sents something. Here is a list of what the various symbols +mean: + +@ This symbol represents you, the adventurer. + +-| These symbols represent the walls of rooms. + ++ A door to/from a room. + +. The floor of a room. + +# The floor of a passage between rooms. + +* A pile or pot of gold. + +) A weapon of some sort. + +] A piece of armor. + +! A flask containing a magic potion. + +? A piece of paper, usually a magic scroll. + += A ring with magic properties + +/ A magical staff or wand + +^ A trap, watch out for these. + +% A staircase to other levels + +: A piece of food. + +A-Z The uppercase letters represent the various inhabitants + of the Dungeons of Doom. Watch out, they can be nasty + and vicious. + +4. Commands + + Commands are given to rogue by typing one or two char- +acters. Most commands can be preceded by a count to repeat +them (e.g. typing "10s" will do ten searches). Commands for +which counts make no sense have the count ignored. To can- +cel a count or a prefix, type . The list of com- +mands is rather long, but it can be read at any time during +the game with the "?" command. Here it is for reference, +with a short explanation of each command. + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-5 + + +? The help command. Asks for a character to give help + on. If you type a "*", it will list all the commands, + otherwise it will explain what the character you typed + does. + +/ This is the "What is that on the screen?" command. A + "/" followed by any character that you see on the + level, will tell you what that character is. For + instance, typing "/@" will tell you that the "@" symbol + represents you, the player. + +h, H, ^H + Move left. You move one space to the left. If you use + upper case "h", you will continue to move left until + you run into something. This works for all movement + commands (e.g. "L" means run in direction "l") If you + use the "control" "h", you will continue moving in the + specified direction until you pass something interest- + ing or run into a wall. You should experiment with + this, since it is a very useful command, but very dif- + ficult to describe. This also works for all movement + commands. + +j Move down. + +k Move up. + +l Move right. + +y Move diagonally up and left. + +u Move diagonally up and right. + +b Move diagonally down and left. + +n Move diagonally down and right. + +t Throw an object. This is a prefix command. When fol- + lowed with a direction it throws an object in the spec- + ified direction. (e.g. type "th" to throw something to + the left.) + +f Fight until someone dies. When followed with a direc- + tion this will force you to fight the creature in that + direction until either you or it bites the big one. + +m Move onto something without picking it up. This will + move you one space in the direction you specify and, if + there is an object there you can pick up, it won't do + it. + +z Zap prefix. Point a staff or wand in a given direction + and fire it. Even non-directional staves must be + + + + + + + + + + +USD:33-6 A Guide to the Dungeons of Doom + + + pointed in some direction to be used. + +^ Identify trap command. If a trap is on your map and + you can't remember what type it is, you can get rogue + to remind you by getting next to it and typing "^" fol- + lowed by the direction that would move you on top of + it. + +s Search for traps and secret doors. Examine each space + immediately adjacent to you for the existence of a trap + or secret door. There is a large chance that even if + there is something there, you won't find it, so you + might have to search a while before you find something. + +> Climb down a staircase to the next level. Not surpris- + ingly, this can only be done if you are standing on + staircase. + +< Climb up a staircase to the level above. This can't be + done without the Amulet of Yendor in your possession. + +. Rest. This is the "do nothing" command. This is good + for waiting and healing. + +, Pick up something. This picks up whatever you are cur- + rently standing on, if you are standing on anything at + all. + +i Inventory. List what you are carrying in your pack. + +I Selective inventory. Tells you what a single item in + your pack is. + +q Quaff one of the potions you are carrying. + +r Read one of the scrolls in your pack. + +e Eat food from your pack. + +w Wield a weapon. Take a weapon out of your pack and + carry it for use in combat, replacing the one you are + currently using (if any). + +W Wear armor. You can only wear one suit of armor at a + time. This takes extra time. + +T Take armor off. You can't remove armor that is cursed. + This takes extra time. + +P Put on a ring. You can wear only two rings at a time + (one on each hand). If you aren't wearing any rings, + this command will ask you which hand you want to wear + it on, otherwise, it will place it on the unused hand. + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-7 + + + The program assumes that you wield your sword in your + right hand. + +R Remove a ring. If you are only wearing one ring, this + command takes it off. If you are wearing two, it will + ask you which one you wish to remove, + +d Drop an object. Take something out of your pack and + leave it lying on the floor. Only one object can + occupy each space. You cannot drop a cursed object at + all if you are wielding or wearing it. + +c Call an object something. If you have a type of object + in your pack which you wish to remember something + about, you can use the call command to give a name to + that type of object. This is usually used when you + figure out what a potion, scroll, ring, or staff is + after you pick it up, or when you want to remember + which of those swords in your pack you were wielding. + +D Print out which things you've discovered something + about. This command will ask you what type of thing + you are interested in. If you type the character for a + given type of object (e.g. "!" for potion) it will + tell you which kinds of that type of object you've dis- + covered (i.e., figured out what they are). This com- + mand works for potions, scrolls, rings, and staves and + wands. + +o Examine and set options. This command is further + explained in the section on options. + +^R Redraws the screen. Useful if spurious messages or + transmission errors have messed up the display. + +^P Print last message. Useful when a message disappears + before you can read it. This only repeats the last + message that was not a mistyped command so that you + don't loose anything by accidentally typing the wrong + character instead of ^P. + + + Cancel a command, prefix, or count. + +! Escape to a shell for some commands. + +Q Quit. Leave the game. + +S Save the current game in a file. It will ask you + whether you wish to use the default save file. Caveat: + Rogue won't let you start up a copy of a saved game, + and it removes the save file as soon as you start up a + restored game. This is to prevent people from saving a + + + + + + + + + + +USD:33-8 A Guide to the Dungeons of Doom + + + game just before a dangerous position and then restart- + ing it if they die. To restore a saved game, give the + file name as an argument to rogue. As in + % rogue save_file + + To restart from the default save file (see below), run + % rogue -r + +v Prints the program version number. + +) Print the weapon you are currently wielding + +] Print the armor you are currently wearing + += Print the rings you are currently wearing + +@ Reprint the status line on the message line + +5. Rooms + + Rooms in the dungeons are either lit or dark. If you +walk into a lit room, the entire room will be drawn on the +screen as soon as you enter. If you walk into a dark room, +it will only be displayed as you explore it. Upon leaving a +room, all monsters inside the room are erased from the +screen. In the darkness you can only see one space in all +directions around you. A corridor is always dark. + +6. Fighting + + If you see a monster and you wish to fight it, just +attempt to run into it. Many times a monster you find will +mind its own business unless you attack it. It is often the +case that discretion is the better part of valor. + +7. Objects you can find + + When you find something in the dungeon, it is common to +want to pick the object up. This is accomplished in rogue +by walking over the object (unless you use the "m" prefix, +see above). If you are carrying too many things, the pro- +gram will tell you and it won't pick up the object, other- +wise it will add it to your pack and tell you what you just +picked up. + + Many of the commands that operate on objects must +prompt you to find out which object you want to use. If you +change your mind and don't want to do that command after +all, just type an and the command will be aborted. + + Some objects, like armor and weapons, are easily dif- +ferentiated. Others, like scrolls and potions, are given +labels which vary according to type. During a game, any two + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-9 + + +of the same kind of object with the same label are the same +type. However, the labels will vary from game to game. + + When you use one of these labeled objects, if its +effect is obvious, rogue will remember what it is for you. +If it's effect isn't extremely obvious you will be asked +what you want to scribble on it so you will recognize it +later, or you can use the "call" command (see above). + +7.1. Weapons + + Some weapons, like arrows, come in bunches, but most +come one at a time. In order to use a weapon, you must +wield it. To fire an arrow out of a bow, you must first +wield the bow, then throw the arrow. You can only wield one +weapon at a time, but you can't change weapons if the one +you are currently wielding is cursed. The commands to use +weapons are "w" (wield) and "t" (throw). + +7.2. Armor + + There are various sorts of armor lying around in the +dungeon. Some of it is enchanted, some is cursed, and some +is just normal. Different armor types have different armor +protection. The higher the armor protection, the more pro- +tection the armor affords against the blows of monsters. +Here is a list of the various armor types and their normal +armor protection: + + + +-----------------------------------------+ + | Type Protection | + |None 0 | + |Leather armor 2 | + |Studded leather / Ring mail 3 | + |Scale mail 4 | + |Chain mail 5 | + |Banded mail / Splint mail 6 | + |Plate mail 7 | + +-----------------------------------------+ + + +If a piece of armor is enchanted, its armor protection will +be higher than normal. If a suit of armor is cursed, its +armor protection will be lower, and you will not be able to +remove it. However, not all armor with a protection that is +lower than normal is cursed. + + The commands to use weapons are "W" (wear) and "T" +(take off). + + + + + + + + + + + + + +USD:33-10 A Guide to the Dungeons of Doom + + +7.3. Scrolls + + Scrolls come with titles in an unknown tongue3. After +you read a scroll, it disappears from your pack. The com- +mand to use a scroll is "r" (read). + +7.4. Potions + + Potions are labeled by the color of the liquid inside +the flask. They disappear after being quaffed. The command +to use a scroll is "q" (quaff). + +7.5. Staves and Wands + + Staves and wands do the same kinds of things. Staves +are identified by a type of wood; wands by a type of metal +or bone. They are generally things you want to do to some- +thing over a long distance, so you must point them at what +you wish to affect to use them. Some staves are not +affected by the direction they are pointed, though. Staves +come with multiple magic charges, the number being random, +and when they are used up, the staff is just a piece of wood +or metal. + + The command to use a wand or staff is "z" (zap) + +7.6. Rings + + Rings are very useful items, since they are relatively +permanent magic, unlike the usually fleeting effects of +potions, scrolls, and staves. Of course, the bad rings are +also more powerful. Most rings also cause you to use up +food more rapidly, the rate varying with the type of ring. +Rings are differentiated by their stone settings. The com- +mands to use rings are "P" (put on) and "R" (remove). + +7.7. Food + + Food is necessary to keep you going. If you go too +long without eating you will faint, and eventually die of +starvation. The command to use food is "e" (eat). + +8. Options + + Due to variations in personal tastes and conceptions of +the way rogue should do things, there are a set of options +you can set that cause rogue to behave in various different +____________________ + 3 Actually, it's a dialect spoken only by the twenty-sev- +en members of a tribe in Outer Mongolia, but you're not sup- +posed to know that. + + + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-11 + + +ways. + +8.1. Setting the options + + There are two ways to set the options. The first is +with the "o" command of rogue; the second is with the +"ROGUEOPTS" environment variable4. + +8.1.1. Using the `o' command + + When you type "o" in rogue, it clears the screen and +displays the current settings for all the options. It then +places the cursor by the value of the first option and waits +for you to type. You can type a which means to go +to the next option, a "-" which means to go to the previous +option, an which means to return to the game, or +you can give the option a value. For boolean options this +merely involves typing "t" for true or "f" for false. For +string options, type the new value followed by a . + +8.1.2. Using the ROGUEOPTS variable + + The ROGUEOPTS variable is a string containing a comma +separated list of initial values for the various options. +Boolean variables can be turned on by listing their name or +turned off by putting a "no" in front of the name. Thus to +set up an environment variable so that jump is on, terse is +off, and the name is set to "Blue Meanie", use the command + % setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"5 + +8.2. Option list + + Here is a list of the options and an explanation of +what each one is for. The default value for each is +enclosed in square brackets. For character string options, +input over fifty characters will be ignored. + +terse [noterse] + Useful for those who are tired of the sometimes lengthy + messages of rogue. This is a useful option for playing + on slow terminals, so this option defaults to terse if + you are on a slow (1200 baud or under) terminal. + + +____________________ + 4 On Version 6 systems, there is no equivalent of the +ROGUEOPTS feature. + 5 For those of you who use the Bourne shell sh (1), the +commands would be + $ ROGUEOPTS="jump,noterse,name=Blue Meanie" + $ export ROGUEOPTS + + + + + + + + + + + + +USD:33-12 A Guide to the Dungeons of Doom + + +jump [nojump] + If this option is set, running moves will not be dis- + played until you reach the end of the move. This saves + considerable cpu and display time. This option + defaults to jump if you are using a slow terminal. + +flush [noflush] + All typeahead is thrown away after each round of bat- + tle. This is useful for those who type far ahead and + then watch in dismay as a Bat kills them. + +seefloor [seefloor] + Display the floor around you on the screen as you move + through dark rooms. Due to the amount of characters + generated, this option defaults to noseefloor if you + are using a slow terminal. + +passgo [nopassgo] + Follow turnings in passageways. If you run in a pas- + sage and you run into stone or a wall, rogue will see + if it can turn to the right or left. If it can only + turn one way, it will turn that way. If it can turn + either or neither, it will stop. This algorithm can + sometimes lead to slightly confusing occurrences which + is why it defaults to nopassgo. + +tombstone [tombstone] + Print out the tombstone at the end if you get killed. + This is nice but slow, so you can turn it off if you + like. + +inven [overwrite] + Inventory type. This can have one of three values: + overwrite, slow, or clear. With overwrite the top + lines of the map are overwritten with the list when + inventory is requested or when "Which item do you wish + to . . .? " questions are answered with a "*". How- + ever, if the list is longer than a screenful, the + screen is cleared. With slow, lists are displayed one + item at a time on the top of the screen, and with + clear, the screen is cleared, the list is displayed, + and then the dungeon level is re-displayed. Due to + speed considerations, clear is the default for termi- + nals without clear-to-end-of-line capabilities. + +name [account name] + This is the name of your character. It is used if you + get on the top ten scorer's list. + +fruit [slime-mold] + This should hold the name of a fruit that you enjoy + eating. It is basically a whimsey that rogue uses in a + couple of places. + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-13 + + +file [~/rogue.save] + The default file name for saving the game. If your + phone is hung up by accident, rogue will automatically + save the game in this file. The file name may start + with the special character "~" which expands to be your + home directory. + +9. Scoring + + Rogue usually maintains a list of the top scoring peo- +ple or scores on your machine. Depending on how it is set +up, it can post either the top scores or the top players. +In the latter case, each account on the machine can post +only one non-winning score on this list. If you score +higher than someone else on this list, or better your previ- +ous score on the list, you will be inserted in the proper +place under your current name. How many scores are kept can +also be set up by whoever installs it on your machine. + + If you quit the game, you get out with all of your gold +intact. If, however, you get killed in the Dungeons of +Doom, your body is forwarded to your next-of-kin, along with +90% of your gold; ten percent of your gold is kept by the +Dungeons' wizard as a fee6. This should make you consider +whether you want to take one last hit at that monster and +possibly live, or quit and thus stop with whatever you have. +If you quit, you do get all your gold, but if you swing and +live, you might find more. + + If you just want to see what the current top play- +ers/games list is, you can type + % rogue -s + +10. Acknowledgements + + Rogue was originally conceived of by Glenn Wichman and +Michael Toy. Ken Arnold and Michael Toy then smoothed out +the user interface, and added jillions of new features. We +would like to thank Bob Arnold, Michelle Busch, Andy +Hatcher, Kipp Hickman, Mark Horton, Daniel Jensen, Bill Joy, +Joe Kalash, Steve Maurer, Marty McNary, Jan Miller, and +Scott Nelson for their ideas and assistance; and also the +teeming multitudes who graciously ignored work, school, and +social life to play rogue and send us bugs, complaints, sug- +gestions, and just plain flames. And also Mom. + + + +____________________ + 6 The Dungeon's wizard is named Wally the Wonder Badger. +Invocations should be accompanied by a sizable donation. + + + + + + + + diff --git a/src/cc/rogue/rogue.doc.in b/src/cc/rogue/rogue.doc.in new file mode 100644 index 000000000..3031d0d24 --- /dev/null +++ b/src/cc/rogue/rogue.doc.in @@ -0,0 +1,858 @@ + + + + + + + + A Guide to the Dungeons of Doom + + + Michael C. Toy + Kenneth C. R. C. Arnold + + + Computer Systems Research Group + Department of Electrical Engineering and Computer Science + University of California + Berkeley, California 94720 + + + + + ABSTRACT + + Rogue is a visual CRT based fantasy game which runs + under the UNIX timesharing system. This paper de- + scribes how to play rogue, and gives a few hints for + those who might otherwise get lost in the Dungeons + of Doom. + + + + +1. Introduction + + You have just finished your years as a student at the +local fighter's guild. After much practice and sweat you +have finally completed your training and are ready to embark +upon a perilous adventure. As a test of your skills, the +local guildmasters have sent you into the Dungeons of Doom. +Your task is to return with the Amulet of Yendor. Your +reward for the completion of this task will be a full mem- +bership in the local guild. In addition, you are allowed to +keep all the loot you bring back from the dungeons. + + In preparation for your journey, you are given an +enchanted mace, a bow, and a quiver of arrows taken from a +dragon's hoard in the far off Dark Mountains. You are also +outfitted with elf-crafted armor and given enough food to +reach the dungeons. You say goodbye to family and friends +for what may be the last time and head up the road. + + You set out on your way to the dungeons and after sev- +eral days of uneventful travel, you see the ancient ruins +that mark the entrance to the Dungeons of Doom. It is late +at night, so you make camp at the entrance and spend the +____________________ + UNIX is a trademark of Bell Laboratories + + + + + + + + + + + + +USD:33-2 A Guide to the Dungeons of Doom + + +night sleeping under the open skies. In the morning you +gather your weapons, put on your armor, eat what is almost +your last food, and enter the dungeons. + +2. What is going on here? + + You have just begun a game of rogue. Your goal is to +grab as much treasure as you can, find the Amulet of Yendor, +and get out of the Dungeons of Doom alive. On the screen, a +map of where you have been and what you have seen on the +current dungeon level is kept. As you explore more of the +level, it appears on the screen in front of you. + + Rogue differs from most computer fantasy games in that +it is screen oriented. Commands are all one or two +keystrokes1 and the results of your commands are displayed +graphically on the screen rather than being explained in +words.2 + + Another major difference between rogue and other com- +puter fantasy games is that once you have solved all the +puzzles in a standard fantasy game, it has lost most of its +excitement and it ceases to be fun. Rogue, on the other +hand, generates a new dungeon every time you play it and +even the author finds it an entertaining and exciting game. + +3. What do all those things on the screen mean? + + In order to understand what is going on in rogue you +have to first get some grasp of what rogue is doing with the +screen. The rogue screen is intended to replace the "You +can see ..." descriptions of standard fantasy games. Figure +1 is a sample of what a rogue screen might look like. + +3.1. The bottom line + + At the bottom line of the screen are a few pieces of +cryptic information describing your current status. Here is +an explanation of what these things mean: + +Level This number indicates how deep you have gone in the + dungeon. It starts at one and goes up as you go + deeper into the dungeon. + +Gold The number of gold pieces you have managed to find + and keep with you so far. +____________________ + 1 As opposed to pseudo English sentences. + 2 A minimum screen size of 24 lines by 80 columns is re- +quired. If the screen is larger, only the 24x80 section +will be used for the map. + + + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-3 + + + +____________________________________________________________ + + + ------------ + |..........+ + |..@....]..| + |....B.....| + |..........| + -----+------ + + + +Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0 + + Figure 1 +____________________________________________________________ + + +Hp Your current and maximum health points. Health + points indicate how much damage you can take before + you die. The more you get hit in a fight, the lower + they get. You can regain health points by resting. + The number in parentheses is the maximum number your + health points can reach. + +Str Your current strength and maximum ever strength. + This can be any integer less than or equal to 31, or + greater than or equal to three. The higher the num- + ber, the stronger you are. The number in the paren- + theses is the maximum strength you have attained so + far this game. + +Arm Your current armor protection. This number indicates + how effective your armor is in stopping blows from + unfriendly creatures. The higher this number is, the + more effective the armor. + +Exp These two numbers give your current experience level + and experience points. As you do things, you gain + experience points. At certain experience point + totals, you gain an experience level. The more expe- + rienced you are, the better you are able to fight and + to withstand magical attacks. + +3.2. The top line + + The top line of the screen is reserved for printing +messages that describe things that are impossible to repre- +sent visually. If you see a "--More--" on the top line, +this means that rogue wants to print another message on the +screen, but it wants to make certain that you have read the +one that is there first. To read the next message, just + + + + + + + + + + +USD:33-4 A Guide to the Dungeons of Doom + + +type a space. + +3.3. The rest of the screen + + The rest of the screen is the map of the level as you +have explored it so far. Each symbol on the screen repre- +sents something. Here is a list of what the various symbols +mean: + +@ This symbol represents you, the adventurer. + +-| These symbols represent the walls of rooms. + ++ A door to/from a room. + +. The floor of a room. + +# The floor of a passage between rooms. + +* A pile or pot of gold. + +) A weapon of some sort. + +] A piece of armor. + +! A flask containing a magic potion. + +? A piece of paper, usually a magic scroll. + += A ring with magic properties + +/ A magical staff or wand + +^ A trap, watch out for these. + +% A staircase to other levels + +: A piece of food. + +A-Z The uppercase letters represent the various inhabitants + of the Dungeons of Doom. Watch out, they can be nasty + and vicious. + +4. Commands + + Commands are given to rogue by typing one or two char- +acters. Most commands can be preceded by a count to repeat +them (e.g. typing "10s" will do ten searches). Commands for +which counts make no sense have the count ignored. To can- +cel a count or a prefix, type . The list of com- +mands is rather long, but it can be read at any time during +the game with the "?" command. Here it is for reference, +with a short explanation of each command. + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-5 + + +? The help command. Asks for a character to give help + on. If you type a "*", it will list all the commands, + otherwise it will explain what the character you typed + does. + +/ This is the "What is that on the screen?" command. A + "/" followed by any character that you see on the + level, will tell you what that character is. For + instance, typing "/@" will tell you that the "@" symbol + represents you, the player. + +h, H, ^H + Move left. You move one space to the left. If you use + upper case "h", you will continue to move left until + you run into something. This works for all movement + commands (e.g. "L" means run in direction "l") If you + use the "control" "h", you will continue moving in the + specified direction until you pass something interest- + ing or run into a wall. You should experiment with + this, since it is a very useful command, but very dif- + ficult to describe. This also works for all movement + commands. + +j Move down. + +k Move up. + +l Move right. + +y Move diagonally up and left. + +u Move diagonally up and right. + +b Move diagonally down and left. + +n Move diagonally down and right. + +t Throw an object. This is a prefix command. When fol- + lowed with a direction it throws an object in the spec- + ified direction. (e.g. type "th" to throw something to + the left.) + +f Fight until someone dies. When followed with a direc- + tion this will force you to fight the creature in that + direction until either you or it bites the big one. + +m Move onto something without picking it up. This will + move you one space in the direction you specify and, if + there is an object there you can pick up, it won't do + it. + +z Zap prefix. Point a staff or wand in a given direction + and fire it. Even non-directional staves must be + + + + + + + + + + +USD:33-6 A Guide to the Dungeons of Doom + + + pointed in some direction to be used. + +^ Identify trap command. If a trap is on your map and + you can't remember what type it is, you can get rogue + to remind you by getting next to it and typing "^" fol- + lowed by the direction that would move you on top of + it. + +s Search for traps and secret doors. Examine each space + immediately adjacent to you for the existence of a trap + or secret door. There is a large chance that even if + there is something there, you won't find it, so you + might have to search a while before you find something. + +> Climb down a staircase to the next level. Not surpris- + ingly, this can only be done if you are standing on + staircase. + +< Climb up a staircase to the level above. This can't be + done without the Amulet of Yendor in your possession. + +. Rest. This is the "do nothing" command. This is good + for waiting and healing. + +, Pick up something. This picks up whatever you are cur- + rently standing on, if you are standing on anything at + all. + +i Inventory. List what you are carrying in your pack. + +I Selective inventory. Tells you what a single item in + your pack is. + +q Quaff one of the potions you are carrying. + +r Read one of the scrolls in your pack. + +e Eat food from your pack. + +w Wield a weapon. Take a weapon out of your pack and + carry it for use in combat, replacing the one you are + currently using (if any). + +W Wear armor. You can only wear one suit of armor at a + time. This takes extra time. + +T Take armor off. You can't remove armor that is cursed. + This takes extra time. + +P Put on a ring. You can wear only two rings at a time + (one on each hand). If you aren't wearing any rings, + this command will ask you which hand you want to wear + it on, otherwise, it will place it on the unused hand. + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-7 + + + The program assumes that you wield your sword in your + right hand. + +R Remove a ring. If you are only wearing one ring, this + command takes it off. If you are wearing two, it will + ask you which one you wish to remove, + +d Drop an object. Take something out of your pack and + leave it lying on the floor. Only one object can + occupy each space. You cannot drop a cursed object at + all if you are wielding or wearing it. + +c Call an object something. If you have a type of object + in your pack which you wish to remember something + about, you can use the call command to give a name to + that type of object. This is usually used when you + figure out what a potion, scroll, ring, or staff is + after you pick it up, or when you want to remember + which of those swords in your pack you were wielding. + +D Print out which things you've discovered something + about. This command will ask you what type of thing + you are interested in. If you type the character for a + given type of object (e.g. "!" for potion) it will + tell you which kinds of that type of object you've dis- + covered (i.e., figured out what they are). This com- + mand works for potions, scrolls, rings, and staves and + wands. + +o Examine and set options. This command is further + explained in the section on options. + +^R Redraws the screen. Useful if spurious messages or + transmission errors have messed up the display. + +^P Print last message. Useful when a message disappears + before you can read it. This only repeats the last + message that was not a mistyped command so that you + don't loose anything by accidentally typing the wrong + character instead of ^P. + + + Cancel a command, prefix, or count. + +! Escape to a shell for some commands. + +Q Quit. Leave the game. + +S Save the current game in a file. It will ask you + whether you wish to use the default save file. Caveat: + Rogue won't let you start up a copy of a saved game, + and it removes the save file as soon as you start up a + restored game. This is to prevent people from saving a + + + + + + + + + + +USD:33-8 A Guide to the Dungeons of Doom + + + game just before a dangerous position and then restart- + ing it if they die. To restore a saved game, give the + file name as an argument to rogue. As in + % rogue save_file + + To restart from the default save file (see below), run + % rogue -r + +v Prints the program version number. + +) Print the weapon you are currently wielding + +] Print the armor you are currently wearing + += Print the rings you are currently wearing + +@ Reprint the status line on the message line + +5. Rooms + + Rooms in the dungeons are either lit or dark. If you +walk into a lit room, the entire room will be drawn on the +screen as soon as you enter. If you walk into a dark room, +it will only be displayed as you explore it. Upon leaving a +room, all monsters inside the room are erased from the +screen. In the darkness you can only see one space in all +directions around you. A corridor is always dark. + +6. Fighting + + If you see a monster and you wish to fight it, just +attempt to run into it. Many times a monster you find will +mind its own business unless you attack it. It is often the +case that discretion is the better part of valor. + +7. Objects you can find + + When you find something in the dungeon, it is common to +want to pick the object up. This is accomplished in rogue +by walking over the object (unless you use the "m" prefix, +see above). If you are carrying too many things, the pro- +gram will tell you and it won't pick up the object, other- +wise it will add it to your pack and tell you what you just +picked up. + + Many of the commands that operate on objects must +prompt you to find out which object you want to use. If you +change your mind and don't want to do that command after +all, just type an and the command will be aborted. + + Some objects, like armor and weapons, are easily dif- +ferentiated. Others, like scrolls and potions, are given +labels which vary according to type. During a game, any two + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-9 + + +of the same kind of object with the same label are the same +type. However, the labels will vary from game to game. + + When you use one of these labeled objects, if its +effect is obvious, rogue will remember what it is for you. +If it's effect isn't extremely obvious you will be asked +what you want to scribble on it so you will recognize it +later, or you can use the "call" command (see above). + +7.1. Weapons + + Some weapons, like arrows, come in bunches, but most +come one at a time. In order to use a weapon, you must +wield it. To fire an arrow out of a bow, you must first +wield the bow, then throw the arrow. You can only wield one +weapon at a time, but you can't change weapons if the one +you are currently wielding is cursed. The commands to use +weapons are "w" (wield) and "t" (throw). + +7.2. Armor + + There are various sorts of armor lying around in the +dungeon. Some of it is enchanted, some is cursed, and some +is just normal. Different armor types have different armor +protection. The higher the armor protection, the more pro- +tection the armor affords against the blows of monsters. +Here is a list of the various armor types and their normal +armor protection: + + + +-----------------------------------------+ + | Type Protection | + |None 0 | + |Leather armor 2 | + |Studded leather / Ring mail 3 | + |Scale mail 4 | + |Chain mail 5 | + |Banded mail / Splint mail 6 | + |Plate mail 7 | + +-----------------------------------------+ + + +If a piece of armor is enchanted, its armor protection will +be higher than normal. If a suit of armor is cursed, its +armor protection will be lower, and you will not be able to +remove it. However, not all armor with a protection that is +lower than normal is cursed. + + The commands to use weapons are "W" (wear) and "T" +(take off). + + + + + + + + + + + + + +USD:33-10 A Guide to the Dungeons of Doom + + +7.3. Scrolls + + Scrolls come with titles in an unknown tongue3. After +you read a scroll, it disappears from your pack. The com- +mand to use a scroll is "r" (read). + +7.4. Potions + + Potions are labeled by the color of the liquid inside +the flask. They disappear after being quaffed. The command +to use a scroll is "q" (quaff). + +7.5. Staves and Wands + + Staves and wands do the same kinds of things. Staves +are identified by a type of wood; wands by a type of metal +or bone. They are generally things you want to do to some- +thing over a long distance, so you must point them at what +you wish to affect to use them. Some staves are not +affected by the direction they are pointed, though. Staves +come with multiple magic charges, the number being random, +and when they are used up, the staff is just a piece of wood +or metal. + + The command to use a wand or staff is "z" (zap) + +7.6. Rings + + Rings are very useful items, since they are relatively +permanent magic, unlike the usually fleeting effects of +potions, scrolls, and staves. Of course, the bad rings are +also more powerful. Most rings also cause you to use up +food more rapidly, the rate varying with the type of ring. +Rings are differentiated by their stone settings. The com- +mands to use rings are "P" (put on) and "R" (remove). + +7.7. Food + + Food is necessary to keep you going. If you go too +long without eating you will faint, and eventually die of +starvation. The command to use food is "e" (eat). + +8. Options + + Due to variations in personal tastes and conceptions of +the way rogue should do things, there are a set of options +you can set that cause rogue to behave in various different +____________________ + 3 Actually, it's a dialect spoken only by the twenty-sev- +en members of a tribe in Outer Mongolia, but you're not sup- +posed to know that. + + + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-11 + + +ways. + +8.1. Setting the options + + There are two ways to set the options. The first is +with the "o" command of rogue; the second is with the +"ROGUEOPTS" environment variable4. + +8.1.1. Using the `o' command + + When you type "o" in rogue, it clears the screen and +displays the current settings for all the options. It then +places the cursor by the value of the first option and waits +for you to type. You can type a which means to go +to the next option, a "-" which means to go to the previous +option, an which means to return to the game, or +you can give the option a value. For boolean options this +merely involves typing "t" for true or "f" for false. For +string options, type the new value followed by a . + +8.1.2. Using the ROGUEOPTS variable + + The ROGUEOPTS variable is a string containing a comma +separated list of initial values for the various options. +Boolean variables can be turned on by listing their name or +turned off by putting a "no" in front of the name. Thus to +set up an environment variable so that jump is on, terse is +off, and the name is set to "Blue Meanie", use the command + % setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"5 + +8.2. Option list + + Here is a list of the options and an explanation of +what each one is for. The default value for each is +enclosed in square brackets. For character string options, +input over fifty characters will be ignored. + +terse [noterse] + Useful for those who are tired of the sometimes lengthy + messages of rogue. This is a useful option for playing + on slow terminals, so this option defaults to terse if + you are on a slow (1200 baud or under) terminal. + + +____________________ + 4 On Version 6 systems, there is no equivalent of the +ROGUEOPTS feature. + 5 For those of you who use the Bourne shell sh (1), the +commands would be + $ ROGUEOPTS="jump,noterse,name=Blue Meanie" + $ export ROGUEOPTS + + + + + + + + + + + + +USD:33-12 A Guide to the Dungeons of Doom + + +jump [nojump] + If this option is set, running moves will not be dis- + played until you reach the end of the move. This saves + considerable cpu and display time. This option + defaults to jump if you are using a slow terminal. + +flush [noflush] + All typeahead is thrown away after each round of bat- + tle. This is useful for those who type far ahead and + then watch in dismay as a Bat kills them. + +seefloor [seefloor] + Display the floor around you on the screen as you move + through dark rooms. Due to the amount of characters + generated, this option defaults to noseefloor if you + are using a slow terminal. + +passgo [nopassgo] + Follow turnings in passageways. If you run in a pas- + sage and you run into stone or a wall, rogue will see + if it can turn to the right or left. If it can only + turn one way, it will turn that way. If it can turn + either or neither, it will stop. This algorithm can + sometimes lead to slightly confusing occurrences which + is why it defaults to nopassgo. + +tombstone [tombstone] + Print out the tombstone at the end if you get killed. + This is nice but slow, so you can turn it off if you + like. + +inven [overwrite] + Inventory type. This can have one of three values: + overwrite, slow, or clear. With overwrite the top + lines of the map are overwritten with the list when + inventory is requested or when "Which item do you wish + to . . .? " questions are answered with a "*". How- + ever, if the list is longer than a screenful, the + screen is cleared. With slow, lists are displayed one + item at a time on the top of the screen, and with + clear, the screen is cleared, the list is displayed, + and then the dungeon level is re-displayed. Due to + speed considerations, clear is the default for termi- + nals without clear-to-end-of-line capabilities. + +name [account name] + This is the name of your character. It is used if you + get on the top ten scorer's list. + +fruit [slime-mold] + This should hold the name of a fruit that you enjoy + eating. It is basically a whimsey that rogue uses in a + couple of places. + + + + + + + + + + +A Guide to the Dungeons of Doom USD:33-13 + + +file [~/rogue.save] + The default file name for saving the game. If your + phone is hung up by accident, rogue will automatically + save the game in this file. The file name may start + with the special character "~" which expands to be your + home directory. + +9. Scoring + + Rogue usually maintains a list of the top scoring peo- +ple or scores on your machine. Depending on how it is set +up, it can post either the top scores or the top players. +In the latter case, each account on the machine can post +only one non-winning score on this list. If you score +higher than someone else on this list, or better your previ- +ous score on the list, you will be inserted in the proper +place under your current name. How many scores are kept can +also be set up by whoever installs it on your machine. + + If you quit the game, you get out with all of your gold +intact. If, however, you get killed in the Dungeons of +Doom, your body is forwarded to your next-of-kin, along with +90% of your gold; ten percent of your gold is kept by the +Dungeons' wizard as a fee6. This should make you consider +whether you want to take one last hit at that monster and +possibly live, or quit and thus stop with whatever you have. +If you quit, you do get all your gold, but if you swing and +live, you might find more. + + If you just want to see what the current top play- +ers/games list is, you can type + % @PROGRAM@ -s + +10. Acknowledgements + + Rogue was originally conceived of by Glenn Wichman and +Michael Toy. Ken Arnold and Michael Toy then smoothed out +the user interface, and added jillions of new features. We +would like to thank Bob Arnold, Michelle Busch, Andy +Hatcher, Kipp Hickman, Mark Horton, Daniel Jensen, Bill Joy, +Joe Kalash, Steve Maurer, Marty McNary, Jan Miller, and +Scott Nelson for their ideas and assistance; and also the +teeming multitudes who graciously ignored work, school, and +social life to play rogue and send us bugs, complaints, sug- +gestions, and just plain flames. And also Mom. + + + +____________________ + 6 The Dungeon's wizard is named Wally the Wonder Badger. +Invocations should be accompanied by a sizable donation. + + + + + + + + diff --git a/src/cc/rogue/rogue.h b/src/cc/rogue/rogue.h new file mode 100644 index 000000000..65ddf1e51 --- /dev/null +++ b/src/cc/rogue/rogue.h @@ -0,0 +1,860 @@ +/* + * Rogue definitions and variable declarations + * + * @(#)rogue.h 5.42 (Berkeley) 08/06/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +#ifndef H_ROGUE_H +#define H_ROGUE_H +#include +#include +#include +#include +#include +#include +#include /* we need va_list */ +#include /* we want wchar_t */ +#include +#include + +#include +#include +#include +#include +#include + +#ifndef BUILD_ROGUE +#include +#else +#include "cursesd.h" +#endif + +#ifdef LINES +#undef LINES +#endif +#ifdef COLS +#undef COLS +#endif + +#define LINES 24 +#define COLS 80 + +#include "extern.h" + + +#undef lines + +#define NOOP(x) (x += 0) +#define CCHAR(x) ( (char) (x & A_CHARTEXT) ) +/* + * Maximum number of different things + */ +#define MAXROOMS 9 +#define MAXTHINGS 9 +#define MAXOBJ 9 +#define MAXPACK 23 +#define MAXTRAPS 10 +#define AMULETLEVEL 26 +#define NUMTHINGS 7 /* number of types of things */ +#define MAXPASS 13 /* upper limit on number of passages */ +#define NUMLINES 24 +#define NUMCOLS 80 +#define STATLINE (NUMLINES - 1) +#define BORE_LEVEL 50 + +/* + * return values for get functions + */ +#define NORM 0 /* normal exit */ +#define QUIT 1 /* quit option setting */ +#define MINUS 2 /* back up one option */ + +/* + * inventory types + */ +#define INV_OVER 0 +#define INV_SLOW 1 +#define INV_CLEAR 2 + +/* + * All the fun defines + */ +#define when break;case +#define otherwise break;default +#define until(expr) while(!(expr)) +#define next(ptr) (*ptr).l_next +#define prev(ptr) (*ptr).l_prev +#define winat(y,x) (moat(y,x) != NULL ? moat(y,x)->t_disguise : chat(y,x)) +#define ce(a,b) ((a).x == (b).x && (a).y == (b).y) +#define hero player.t_pos +#define pstats player.t_stats +#define pack player.t_pack +#define proom player.t_room +#define max_hp player.t_stats.s_maxhp +#define attach(a,b) _attach(&a,b) +#define detach(a,b) _detach(&a,b) +#define free_list(a) _free_list(&a) +#undef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#define on(thing,flag) ((bool)(((thing).t_flags & (flag)) != 0)) +#define GOLDCALC (rnd(50 + 10 * level) + 2) +#define ISRING(h,r) (cur_ring[h] != NULL && cur_ring[h]->o_which == r) +#define ISWEARING(r) (ISRING(LEFT, r) || ISRING(RIGHT, r)) +#define ISMULT(type) (type == POTION || type == SCROLL || type == FOOD) +#define INDEX(y,x) (&places[((x) << 5) + (y)]) +#define chat(y,x) (places[((x) << 5) + (y)].p_ch) +#define flat(y,x) (places[((x) << 5) + (y)].p_flags) +#define moat(y,x) (places[((x) << 5) + (y)].p_monst) +#define unc(cp) (cp).y, (cp).x +#ifdef MASTER +#define debug if (wizard) msg +#endif + +/* + * things that appear on the screens + */ +#define PASSAGE '#' +#define DOOR '+' +#define FLOOR '.' +#define PLAYER '@' +#define TRAP '^' +#define STAIRS '%' +#define GOLD '*' +#define POTION '!' +#define SCROLL '?' +#define MAGIC '$' +#define FOOD ':' +#define WEAPON ')' +#define ARMOR ']' +#define AMULET ',' +#define RING '=' +#define STICK '/' +#define CALLABLE -1 +#define R_OR_S -2 + +/* + * Various constants + */ +#define BEARTIME spread(3) +#define SLEEPTIME spread(5) +#define HOLDTIME spread(2) +#define WANDERTIME spread(70) +#define BEFORE spread(1) +#define AFTER spread(2) +#define HEALTIME 30 +#define HUHDURATION 20 +#define SEEDURATION 850 +#define HUNGERTIME 1300 +#define MORETIME 150 +#define STOMACHSIZE 2000 +#define STARVETIME 850 +#define ESCAPE 27 +#define LEFT 0 +#define RIGHT 1 +#define BOLT_LENGTH 6 +#define LAMPDIST 3 +#ifdef MASTER +#ifndef PASSWD +#define PASSWD "mTBellIQOsLNA" +#endif +#endif + +/* + * Save against things + */ +#define VS_POISON 00 +#define VS_PARALYZATION 00 +#define VS_DEATH 00 +#define VS_BREATH 02 +#define VS_MAGIC 03 + +/* + * Various flag bits + */ +/* flags for rooms */ +#define ISDARK 0000001 /* room is dark */ +#define ISGONE 0000002 /* room is gone (a corridor) */ +#define ISMAZE 0000004 /* room is gone (a corridor) */ + +/* flags for objects */ +#define ISCURSED 000001 /* object is cursed */ +#define ISKNOW 0000002 /* player knows details about the object */ +#define ISMISL 0000004 /* object is a missile type */ +#define ISMANY 0000010 /* object comes in groups */ +/* ISFOUND 0000020 ...is used for both objects and creatures */ +#define ISPROT 0000040 /* armor is permanently protected */ + +/* flags for creatures */ +#define CANHUH 0000001 /* creature can confuse */ +#define CANSEE 0000002 /* creature can see invisible creatures */ +#define ISBLIND 0000004 /* creature is blind */ +#define ISCANC 0000010 /* creature has special qualities cancelled */ +#define ISLEVIT 0000010 /* hero is levitating */ +#define ISFOUND 0000020 /* creature has been seen (used for objects) */ +#define ISGREED 0000040 /* creature runs to protect gold */ +#define ISHASTE 0000100 /* creature has been hastened */ +#define ISTARGET 000200 /* creature is the target of an 'f' command */ +#define ISHELD 0000400 /* creature has been held */ +#define ISHUH 0001000 /* creature is confused */ +#define ISINVIS 0002000 /* creature is invisible */ +#define ISMEAN 0004000 /* creature can wake when player enters room */ +#define ISHALU 0004000 /* hero is on acid trip */ +#define ISREGEN 0010000 /* creature can regenerate */ +#define ISRUN 0020000 /* creature is running at the player */ +#define SEEMONST 040000 /* hero can detect unseen monsters */ +#define ISFLY 0040000 /* creature can fly */ +#define ISSLOW 0100000 /* creature has been slowed */ + +/* + * Flags for level map + */ +#define F_PASS 0x80 /* is a passageway */ +#define F_SEEN 0x40 /* have seen this spot before */ +#define F_DROPPED 0x20 /* object was dropped here */ +#define F_LOCKED 0x20 /* door is locked */ +#define F_REAL 0x10 /* what you see is what you get */ +#define F_PNUM 0x0f /* passage number mask */ +#define F_TMASK 0x07 /* trap number mask */ + +/* + * Trap types + */ +#define T_DOOR 00 +#define T_ARROW 01 +#define T_SLEEP 02 +#define T_BEAR 03 +#define T_TELEP 04 +#define T_DART 05 +#define T_RUST 06 +#define T_MYST 07 +#define NTRAPS 8 + +/* + * Potion types + */ +#define P_CONFUSE 0 +#define P_LSD 1 +#define P_POISON 2 +#define P_STRENGTH 3 +#define P_SEEINVIS 4 +#define P_HEALING 5 +#define P_MFIND 6 +#define P_TFIND 7 +#define P_RAISE 8 +#define P_XHEAL 9 +#define P_HASTE 10 +#define P_RESTORE 11 +#define P_BLIND 12 +#define P_LEVIT 13 +#define MAXPOTIONS 14 + +/* + * Scroll types + */ +#define S_CONFUSE 0 +#define S_MAP 1 +#define S_HOLD 2 +#define S_SLEEP 3 +#define S_ARMOR 4 +#define S_ID_POTION 5 +#define S_ID_SCROLL 6 +#define S_ID_WEAPON 7 +#define S_ID_ARMOR 8 +#define S_ID_R_OR_S 9 +#define S_SCARE 10 +#define S_FDET 11 +#define S_TELEP 12 +#define S_ENCH 13 +#define S_CREATE 14 +#define S_REMOVE 15 +#define S_AGGR 16 +#define S_PROTECT 17 +#define MAXSCROLLS 18 + +/* + * Weapon types + */ +#define MACE 0 +#define SWORD 1 +#define BOW 2 +#define ARROW 3 +#define DAGGER 4 +#define TWOSWORD 5 +#define DART 6 +#define SHIRAKEN 7 +#define SPEAR 8 +#define FLAME 9 /* fake entry for dragon breath (ick) */ +#define MAXWEAPONS 9 /* this should equal FLAME */ + +/* + * Armor types + */ +#define LEATHER 0 +#define RING_MAIL 1 +#define STUDDED_LEATHER 2 +#define SCALE_MAIL 3 +#define CHAIN_MAIL 4 +#define SPLINT_MAIL 5 +#define BANDED_MAIL 6 +#define PLATE_MAIL 7 +#define MAXARMORS 8 + +/* + * Ring types + */ +#define R_PROTECT 0 +#define R_ADDSTR 1 +#define R_SUSTSTR 2 +#define R_SEARCH 3 +#define R_SEEINVIS 4 +#define R_NOP 5 +#define R_AGGR 6 +#define R_ADDHIT 7 +#define R_ADDDAM 8 +#define R_REGEN 9 +#define R_DIGEST 10 +#define R_TELEPORT 11 +#define R_STEALTH 12 +#define R_SUSTARM 13 +#define MAXRINGS 14 + +/* + * Rod/Wand/Staff types + */ +#define WS_LIGHT 0 +#define WS_INVIS 1 +#define WS_ELECT 2 +#define WS_FIRE 3 +#define WS_COLD 4 +#define WS_POLYMORPH 5 +#define WS_MISSILE 6 +#define WS_HASTE_M 7 +#define WS_SLOW_M 8 +#define WS_DRAIN 9 +#define WS_NOP 10 +#define WS_TELAWAY 11 +#define WS_TELTO 12 +#define WS_CANCEL 13 +#define MAXSTICKS 14 + +/* + * Now we define the structures and types + */ + +#define SMALLVAL 0.000000000000001 +#define SATOSHIDEN ((uint64_t)100000000L) +#define dstr(x) ((double)(x) / SATOSHIDEN) + +#ifndef _BITS256 +#define _BITS256 +union _bits256 { uint8_t bytes[32]; uint16_t ushorts[16]; uint32_t uints[8]; uint64_t ulongs[4]; uint64_t txid; }; +typedef union _bits256 bits256; +#endif + +#include "rogue_player.h" // interface to rpc + +struct rogue_state +{ + uint64_t seed; + char *keystrokes,*keystrokeshex; + uint32_t needflush,replaydone; + int32_t numkeys,ind,num,guiflag,counter,sleeptime,playersize,restoring,lastnum; + FILE *logfp; + struct rogue_player P; + char buffered[10000]; + uint8_t playerdata[10000]; +}; +extern struct rogue_state globalR; + +int rogue(int argc, char **argv, char **envp); +void rogueiterate(struct rogue_state *rs); +int32_t roguefname(char *fname,uint64_t seed,int32_t counter); +int32_t flushkeystrokes(struct rogue_state *rs,int32_t waitflag); +int32_t rogue_restorepack(struct rogue_state *rs); +void restore_player(struct rogue_state *rs); +int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis); +void rogue_bailout(struct rogue_state *rs); +int32_t rogue_progress(struct rogue_state *rs,int32_t waitflag,uint64_t seed,char *keystrokes,int32_t num); +int32_t rogue_setplayerdata(struct rogue_state *rs,char *gametxidstr); + +#define ROGUE_MAXTOTAL (pstats.s_str*2) + +/* + * Help list + */ +struct h_list { + char h_ch; + char *h_desc; + bool h_print; +}; + +/* + * Coordinate data type + */ +typedef struct { + int x; + int y; +} coord; + +typedef unsigned int str_t; + +/* + * Stuff about objects + */ +struct obj_info { + char *oi_name; + int oi_prob; + int oi_worth; + char *oi_guess; + bool oi_know; +}; + +/* + * Room structure + */ +struct room { + coord r_pos; /* Upper left corner */ + coord r_max; /* Size of room */ + coord r_gold; /* Where the gold is */ + int r_goldval; /* How much the gold is worth */ + short r_flags; /* info about the room */ + int r_nexits; /* Number of exits */ + coord r_exit[12]; /* Where the exits are */ +}; + +/* + * Structure describing a fighting being + */ +struct stats { + str_t s_str; /* Strength */ + int s_exp; /* Experience */ + int s_lvl; /* level of mastery */ + int s_arm; /* Armor class */ + int s_hpt; /* Hit points */ + char s_dmg[13]; /* String describing damage done */ + int s_maxhp; /* Max hit points */ +}; + +/* + * Structure for monsters and player + */ +union thing { + struct { + union thing *_l_next, *_l_prev; /* Next pointer in link */ + coord _t_pos; /* Position */ + bool _t_turn; /* If slowed, is it a turn to move */ + char _t_type; /* What it is */ + char _t_disguise; /* What mimic looks like */ + char _t_oldch; /* Character that was where it was */ + coord *_t_dest; /* Where it is running to */ + short _t_flags; /* State word */ + struct stats _t_stats; /* Physical description */ + struct room *_t_room; /* Current room for thing */ + union thing *_t_pack; /* What the thing is carrying */ + int _t_reserved; + } _t; + struct { + union thing *_l_next, *_l_prev; /* Next pointer in link */ + int _o_type; /* What kind of object it is */ + coord _o_pos; /* Where it lives on the screen */ + char *_o_text; /* What it says if you read it */ + int _o_launch; /* What you need to launch it */ + char _o_packch; /* What character it is in the pack */ + char _o_damage[8]; /* Damage if used like sword */ + char _o_hurldmg[8]; /* Damage if thrown */ + int _o_count; /* count for plural objects */ + int _o_which; /* Which object of a type it is */ + int _o_hplus; /* Plusses to hit */ + int _o_dplus; /* Plusses to damage */ + int _o_arm; /* Armor protection */ + int _o_flags; /* information about objects */ + int _o_group; /* group number for this object */ + char *_o_label; /* Label for object */ + } _o; +}; + +typedef union thing THING; + +#define l_next _t._l_next +#define l_prev _t._l_prev +#define t_pos _t._t_pos +#define t_turn _t._t_turn +#define t_type _t._t_type +#define t_disguise _t._t_disguise +#define t_oldch _t._t_oldch +#define t_dest _t._t_dest +#define t_flags _t._t_flags +#define t_stats _t._t_stats +#define t_pack _t._t_pack +#define t_room _t._t_room +#define t_reserved _t._t_reserved +#define o_type _o._o_type +#define o_pos _o._o_pos +#define o_text _o._o_text +#define o_launch _o._o_launch +#define o_packch _o._o_packch +#define o_damage _o._o_damage +#define o_hurldmg _o._o_hurldmg +#define o_count _o._o_count +#define o_which _o._o_which +#define o_hplus _o._o_hplus +#define o_dplus _o._o_dplus +#define o_arm _o._o_arm +#define o_charges o_arm +#define o_goldval o_arm +#define o_flags _o._o_flags +#define o_group _o._o_group +#define o_label _o._o_label + +/* + * describe a place on the level map + */ +typedef struct { + char p_ch; + char p_flags; + THING *p_monst; +} PLACE; + +/* + * Array containing information on all the various types of monsters + */ +struct monster { + char *m_name; /* What to call the monster */ + int m_carry; /* Probability of carrying something */ + short m_flags; /* things about the monster */ + struct stats m_stats; /* Initial stats */ +}; + +/* + * External variables + */ +extern const char *tr_name[],*inv_t_name[]; +extern const int32_t a_class[], e_levels[]; +extern const struct h_list helpstr[]; +extern const char *h_names[],*m_names[]; + + +extern const struct monster origmonsters[26]; +extern const struct room origpassages[MAXPASS]; +extern const struct obj_info origthings[NUMTHINGS],origring_info[MAXRINGS],origpot_info[MAXPOTIONS],origarm_info[MAXARMORS],origscr_info[MAXSCROLLS],origws_info[MAXSTICKS],origweap_info[MAXWEAPONS + 1]; +extern struct monster monsters[26]; +extern struct room passages[MAXPASS]; +extern struct obj_info things[NUMTHINGS],ring_info[MAXRINGS],pot_info[MAXPOTIONS],arm_info[MAXARMORS],scr_info[MAXSCROLLS],weap_info[MAXWEAPONS + 1],ws_info[MAXSTICKS]; + +extern bool after, again, allscore, amulet, door_stop, fight_flush, + firstmove, has_hit, inv_describe, jump, kamikaze, + lower_msg, move_on, msg_esc, pack_used[], + passgo, playing, q_comm, running, save_msg, see_floor, + seenstairs, stat_msg, terse, to_death, tombstone; + +extern char dir_ch, file_name[], home[], huh[], + l_last_comm, l_last_dir, last_comm, last_dir, *Numname, + outbuf[], *release, *s_names[], runch, take; +extern const char *ws_made[], *r_stones[], *p_colors[], *ws_type[]; + +extern int count, food_left, hungry_state, inpack, + inv_type, lastscore, level, max_hit, max_level, mpos, + n_objs, no_command, no_food, no_move, noscore, ntraps, purse, + quiet, vf_hit; + +extern unsigned int numscores; + +extern uint64_t seed; + +extern WINDOW *hw; + +extern coord delta, oldpos, stairs; + +extern PLACE places[]; + +extern THING *cur_armor, *cur_ring[], *cur_weapon, *l_last_pick, + *last_pick, *lvl_obj, *mlist, player; + + +extern struct room *oldrp, rooms[]; + +extern struct stats max_stats; + + +/* + * Function types + */ +void _attach(THING **list, THING *item); +void _detach(THING **list, THING *item); +void _free_list(THING **ptr); +void addmsg(struct rogue_state *rs,char *fmt, ...); +bool add_haste(struct rogue_state *rs,bool potion); +char add_line(struct rogue_state *rs,char *fmt, char *arg); +void add_pack(struct rogue_state *rs,THING *obj, bool silent); +void add_pass(void); +void add_str(str_t *sp, int amt); +void accnt_maze(int y, int x, int ny, int nx); +void aggravate(struct rogue_state *rs); +int attack(struct rogue_state *rs,THING *mp); +void badcheck(char *name, struct obj_info *info, int bound); +void bounce(struct rogue_state *rs,THING *weap, char *mname, bool noend); +void call(struct rogue_state *rs); +void call_it(struct rogue_state *rs,struct obj_info *info); +bool cansee(struct rogue_state *rs,int y, int x); +int center(char *str); +void chg_str(int amt); +void check_level(struct rogue_state *rs); +void conn(struct rogue_state *rs,int r1, int r2); +void command(struct rogue_state *rs); +void create_obj(struct rogue_state *rs); + +void current(struct rogue_state *rs,THING *cur, char *how, char *where); +void d_level(struct rogue_state *rs); +void death(struct rogue_state *rs,char monst); +char death_monst(void); +void dig(struct rogue_state *rs,int y, int x); +void discard(THING *item); +void discovered(struct rogue_state *rs); +int dist(int y1, int x1, int y2, int x2); +int dist_cp(coord *c1, coord *c2); +int do_chase(struct rogue_state *rs,THING *th); +void do_daemons(struct rogue_state *rs,int flag); +void do_fuses(struct rogue_state *rs,int flag); +void do_maze(struct rogue_state *rs,struct room *rp); +void do_motion(struct rogue_state *rs,THING *obj, int ydelta, int xdelta); +void do_move(struct rogue_state *rs,int dy, int dx); +void do_passages(struct rogue_state *rs); +void do_pot(struct rogue_state *rs,int type, bool knowit); +void do_rooms(struct rogue_state *rs); +void do_run(char ch); +void do_zap(struct rogue_state *rs); +void doadd(struct rogue_state *rs,char *fmt, va_list args); +void door(struct room *rm, coord *cp); +void door_open(struct rogue_state *rs,struct room *rp); +void drain(struct rogue_state *rs); +void draw_room(struct rogue_state *rs,struct room *rp); +void drop(struct rogue_state *rs); +void eat(struct rogue_state *rs); +size_t encread(char *start, size_t size, FILE *inf); +size_t encwrite(char *start, size_t size, FILE *outf); +int endmsg(struct rogue_state *rs); +void enter_room(struct rogue_state *rs,coord *cp); +void erase_lamp(coord *pos, struct room *rp); +int exp_add(THING *tp); +void extinguish(void (*func)(struct rogue_state *rs,int)); +void fall(struct rogue_state *rs,THING *obj, bool pr); +void fire_bolt(struct rogue_state *rs,coord *start, coord *dir, char *name); +char floor_at(void); +void flush_type(void); +int fight(struct rogue_state *rs,coord *mp, THING *weap, bool thrown); +void fix_stick(THING *cur); +void fuse(void (*func)(struct rogue_state *rs,int), int arg, int time, int type); +bool get_dir(struct rogue_state *rs); +int gethand(struct rogue_state *rs); +void give_pack(struct rogue_state *rs,THING *tp); +void help(struct rogue_state *rs); +void hit(struct rogue_state *rs,char *er, char *ee, bool noend); +void horiz(struct room *rp, int starty); +void leave_room(struct rogue_state *rs,coord *cp); +void lengthen(void (*func)(struct rogue_state *rs,int), int xtime); +void look(struct rogue_state *rs,bool wakeup); +int hit_monster(struct rogue_state *rs,int y, int x, THING *obj); +void identify(struct rogue_state *rs); +void illcom(struct rogue_state *rs,int ch); +void init_check(void); +void init_colors(void); +void init_materials(void); +void init_names(void); +void init_player(struct rogue_state *rs); +void init_probs(void); +void init_stones(void); +void init_weapon(THING *weap, int which); +bool inventory(struct rogue_state *rs,THING *list, int type); +void invis_on(void); +void killed(struct rogue_state *rs,THING *tp, bool pr); +void kill_daemon(void (*func)(struct rogue_state *rs,int)); +bool lock_sc(void); +void miss(struct rogue_state *rs,char *er, char *ee, bool noend); +void missile(struct rogue_state *rs,int ydelta, int xdelta); +void money(struct rogue_state *rs,int value); +int move_monst(struct rogue_state *rs,THING *tp); +void move_msg(struct rogue_state *rs,THING *obj); +int msg(struct rogue_state *rs,char *fmt, ...); +void nameit(THING *obj, const char *type, const char *which, struct obj_info *op, char *(*prfunc)(THING *)); +void new_level(struct rogue_state *rs); +void new_monster(struct rogue_state *rs,THING *tp, char type, coord *cp); +void numpass(int y, int x); +void option(struct rogue_state *rs); +void open_score(void); +void parse_opts(char *str); +void passnum(void); +char *pick_color(char *col); +int pick_one(struct rogue_state *rs,struct obj_info *info, int nitems); +void pick_up(struct rogue_state *rs,char ch); +void picky_inven(struct rogue_state *rs); +void pr_spec(struct obj_info *info, int nitems); +void pr_list(void); +void put_bool(void *b); +void put_inv_t(void *ip); +void put_str(void *str); +void put_things(struct rogue_state *rs); +void putpass(coord *cp); +void print_disc(struct rogue_state *rs,char); +void quaff(struct rogue_state *rs); +void raise_level(struct rogue_state *rs); +char randmonster(bool wander); +void read_scroll(struct rogue_state *rs); +void relocate(struct rogue_state *rs,THING *th, coord *new_loc); +void remove_mon(struct rogue_state *rs,coord *mp, THING *tp, bool waskill); +void reset_last(void); +bool restore(struct rogue_state *rs,char *file, char **envp); +int ring_eat(int hand); +void ring_on(struct rogue_state *rs); +void ring_off(struct rogue_state *rs); +int rnd(int range); +int rnd_room(void); +int roll(int number, int sides); +int rs_save_file(struct rogue_state *rs,FILE *savef); +int rs_restore_file(FILE *inf); +void runto(struct rogue_state *rs,coord *runner); +void rust_armor(struct rogue_state *rs,THING *arm); +int save(int which); +void save_file(struct rogue_state *rs,FILE *savef,int32_t guiflag); +void save_game(struct rogue_state *rs); +int save_throw(int which, THING *tp); +void score(struct rogue_state *rs,int amount, int flags, char monst); +void search(struct rogue_state *rs); +void set_know(THING *obj, struct obj_info *info); +void set_oldch(THING *tp, coord *cp); +void setup(void); +void shell(struct rogue_state *rs); +bool show_floor(void); +void show_map(void); +void show_win(struct rogue_state *rs,char *message); +int sign(int nm); +int spread(int nm); +void start_daemon(void (*func)(struct rogue_state *rs,int), int arg, int type); +void start_score(void); +void status(struct rogue_state *rs); +int step_ok(int ch); +void strucpy(char *s1, char *s2, int len); +int swing(int at_lvl, int op_arm, int wplus); +void take_off(struct rogue_state *rs); +void teleport(struct rogue_state *rs); +void total_winner(struct rogue_state *rs); +void thunk(struct rogue_state *rs,THING *weap, char *mname, bool noend); +void treas_room(struct rogue_state *rs); +void turnref(void); +void u_level(struct rogue_state *rs); +void uncurse(THING *obj); +void unlock_sc(void); +void vert(struct room *rp, int startx); +void wait_for(struct rogue_state *rs,int ch); +THING *wake_monster(struct rogue_state *rs,int y, int x); +void wanderer(struct rogue_state *rs); +void waste_time(struct rogue_state *rs); +void wear(struct rogue_state *rs); +void whatis(struct rogue_state *rs,bool insist, int type); +void wield(struct rogue_state *rs); + +bool chase(THING *tp, coord *ee); +bool diag_ok(coord *sp, coord *ep); +bool dropcheck(struct rogue_state *rs,THING *obj); +bool fallpos(coord *pos, coord *newpos); +bool find_floor(struct rogue_state *rs,struct room *rp, coord *cp, int limit, bool monst); +bool is_magic(THING *obj); +bool is_symlink(char *sp); +bool levit_check(struct rogue_state *rs); +bool pack_room(struct rogue_state *rs,bool from_floor, THING *obj); +bool roll_em(THING *thatt, THING *thdef, THING *weap, bool hurl); +bool see_monst(THING *mp); +bool seen_stairs(void); +bool turn_ok(int y, int x); +bool turn_see(struct rogue_state *rs,bool turn_off); +bool is_current(struct rogue_state *rs,THING *obj); +int passwd(void); + +char be_trapped(struct rogue_state *rs,coord *tc); +char floor_ch(void); +char pack_char(void); +char readchar(struct rogue_state *rs); +char rnd_thing(void); + +char *charge_str(THING *obj); +char *choose_str(char *ts, char *ns); +char *inv_name(THING *obj, bool drop); +char *nullstr(THING *ignored); +char *num(int n1, int n2, char type); +char *ring_num(THING *obj); +char *set_mname(THING *tp); +char *vowelstr(char *str); + +int get_bool(struct rogue_state *rs,void *vp, WINDOW *win); +int get_inv_t(struct rogue_state *rs,void *vp, WINDOW *win); +int get_num(struct rogue_state *rs,void *vp, WINDOW *win); +int get_sf(struct rogue_state *rs,void *vp, WINDOW *win); +int get_str(struct rogue_state *rs,void *vopt, WINDOW *win); +int trip_ch(int y, int x, int ch); + +coord *find_dest(struct rogue_state *rs,THING *tp); +coord *rndmove(THING *who); + +THING *find_obj(struct rogue_state *rs,int y, int x); +THING *get_item(struct rogue_state *rs,char *purpose, int type); +THING *leave_pack(struct rogue_state *rs,THING *obj, bool newobj, bool all); +THING *new_item(void); +THING *new_thing(struct rogue_state *rs); +void end_line(struct rogue_state *rs); +int32_t num_packitems(struct rogue_state *rs); +int32_t rogue_total(THING *o); + +void runners(struct rogue_state *rs,int); +void land(struct rogue_state *rs,int); +void visuals(struct rogue_state *rs,int); +void come_down(struct rogue_state *rs,int); +void stomach(struct rogue_state *rs,int); +void nohaste(struct rogue_state *rs,int); +void sight(struct rogue_state *rs,int); +void unconfuse(struct rogue_state *rs,int); +void rollwand(struct rogue_state *rs,int); +void unsee(struct rogue_state *rs,int); +void swander(struct rogue_state *rs,int); +void doctor(struct rogue_state *rs,int); + +void playit(struct rogue_state *rs); + +struct room *roomin(struct rogue_state *rs,coord *cp); +int32_t thing_find(THING *ptr); + +#define MAXDAEMONS 20 + +extern struct delayed_action { + int d_type; + void (*d_func)(struct rogue_state *rs,int); + int d_arg; + int d_time; +} d_list[MAXDAEMONS]; + +typedef struct { + char *st_name; + int st_value; +} STONE; + +extern int total; +extern int between; +extern int group; +extern coord nh; +extern const char *rainbow[]; +extern int cNCOLORS; +extern const STONE stones[]; +extern int cNSTONES; +extern const char *wood[]; +extern int cNWOOD; +extern const char *metal[]; +extern int cNMETAL; + +//extern WINDOW *stdscr,*curscr; + +#endif + diff --git a/src/cc/rogue/rogue.html b/src/cc/rogue/rogue.html new file mode 100644 index 000000000..00dab46bf --- /dev/null +++ b/src/cc/rogue/rogue.html @@ -0,0 +1,1060 @@ + + + + + + + + + + +

A Guide to the Dungeons of Doom

+ +

Michael C. Toy
+Kenneth C. R. C. Arnold

+ +

Computer Systems Research +Group
+Department of Electrical Engineering and Computer +Science
+University of California
+Berkeley, California 94720

+ + +

ABSTRACT

+ +
+
+

Rogue is a visual CRT based fantasy game which runs under the + UNIX† timesharing system. This paper describes how + to play rogue, and gives a few hints for those who might otherwise get + lost in the Dungeons of Doom.

+
+
+ +

1. Introduction

+ +

You have just finished your years as a +student at the local fighter’s guild. After much +practice and sweat you have finally completed your training +and are ready to embark upon a perilous adventure. As a test +of your skills, the local guildmasters have sent you into +the Dungeons of Doom. Your task is to return with the Amulet +of Yendor. Your reward for the completion of this task will +be a full membership in the local guild. In addition, you +are allowed to keep all the loot you bring back from the +dungeons.

+ +

In preparation for your journey, you are +given an enchanted mace, a bow, and a quiver of arrows taken +from a dragon’s hoard in the far off Dark Mountains. +You are also outfitted with elf-crafted armor and given +enough food to reach the dungeons. You say goodbye to family +and friends for what may be the last time and head up the +road.

+ +

You set out on your way to the dungeons and +after several days of uneventful travel, you see the +ancient ruins that mark the entrance to the Dungeons of +Doom. It is late at night, so you make camp at the entrance +and spend the night sleeping under the open skies. In the +morning you gather your weapons, put on your armor, eat what +is almost your last food, and enter the +dungeons.

+ +

2. What is going on here?

+

You have just begun a game of rogue. Your +goal is to grab as much treasure as you can, find the Amulet +of Yendor, and get out of the Dungeons of Doom alive. On the +screen, a map of where you have been and what you have seen +on the current dungeon level is kept. As you explore more of +the level, it appears on the screen in front of +you.

+

Rogue differs from most computer fantasy +games in that it is screen oriented. Commands are all one or +two keystrokes1 and the +results of your commands are displayed graphically on the +screen rather than being explained in words2.

+

Another major difference between rogue and other computer fantasy games is that once you have solved +all the puzzles in a standard fantasy game, it has lost most +of its excitement and it ceases to be fun. Rogue, on the +other hand, generates a new dungeon every time you play it +and even the author finds it an entertaining and exciting +game.

+ +

3. What do all those things on the screen mean?

+

In order to understand what is going on in +rogue you have to first get some grasp of what rogue is +doing with the screen. The rogue screen is intended to +replace the “You can see ...” descriptions of +standard fantasy games. Figure 1 is a sample of what a +rogue screen might look like.

+ +
+ +
+
+____________________________________________________________
+
+
+                        ------------
+                        |..........+
+                        |..@....]..|
+                        |....B.....|
+                        |..........|
+                        -----+------
+
+
+
+Level: 1  Gold: 0      Hp: 12(12)  Str: 16(16)  Arm: 4  Exp: 1/0
+
+                          Figure 1
+____________________________________________________________
+
+
+
+

3.1. The bottom line

+

At the bottom line of the screen are a few +pieces of cryptic information describing your current +status. Here is an explanation of what these things +mean:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Level

+

This number indicates how deep you +have gone in the dungeon. It starts at one and goes up as +you go deeper into the dungeon.

+

Gold

+

The number of gold pieces you have managed to find and keep with you + so far.

+

Hp

+

Your current and maximum health points. +Health points indicate how much damage you can take before +you die. The more you get hit in a fight, the lower they +get. You can regain health points by resting. The number in +parentheses is the maximum number your health points can +reach.

+ +
+

Str

+ +

Your current strength and maximum ever +strength. This can be any integer less than or equal to 31, +or greater than or equal to three. The higher the num- ber, +the stronger you are. The number in the parentheses is the +maximum strength you have attained so far this +game.

+ +
+

Arm

+

Your current armor protection. This +number indicates how effective your armor is in stopping +blows from unfriendly creatures. The higher this number is, +the more effective the armor.

+ +
+

Exp

+

These two numbers give your current +experience level and experience points. As you do things, +you gain experience points. At certain experience point +totals, you gain an experience level. The more experienced +you are, the better you are able to fight and to withstand +magical attacks.

+ +
+ +

 

+ +

3.2. The top line

+

The top line of the screen is reserved for +printing messages that describe things that are impossible +to represent visually. If you see a “--More--” +on the top line, this means that rogue wants to print +another message on the screen, but it wants to make certain +that you have read the one that is there first. To read the +next message, just type a space.

+ + +

3.3. The rest of the screen

+

The rest of the screen is the map of the +level as you have explored it so far. Each symbol on the +screen repre- sents something. Here is a list of what the +various symbols mean:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

@

+

This symbol represents you, the adventurer.

+

- |

+

These symbols represent the walls of rooms.

+

+

+

A door to/from a room.

+

.

+

The floor of a room.

+

#

+

The floor of a passage between rooms.

+

*

+

A pile or pot of gold.

+

)

+

A weapon of some sort.

+

]

+

A piece of armor.

+

!

+

A flask containing a magic potion.

+

?

+

A piece of paper, usually a magic scroll.

+

=

+

A ring with magic properties

+

/

+

A magical staff or wand

+

^

+

A trap, watch out for these.

+

%

+

A staircase to other levels

+

:

+

A piece of food.

+

A-Z

+

The uppercase letters represent the various + inhabitants of the Dungeons of Doom. Watch out, they can be nasty and + vicious.

+ + +

4. Commands

+

Commands are given to rogue by typing one or two characters. +Most commands can be preceded by a count to repeat them (e.g. typing “10s” will +do ten searches). Commands for which counts make no sense have the count +ignored. To cancel a count or a prefix, type <ESCAPE> . The list of commands is +rather long, but it can be read at any time during the game +with the “?” command. Here it is for reference, +with a short explanation of each +command.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

?

+

The help command. Asks for a character to give help + on. If you type a “*”, it will list all the commands, otherwise it will + explain what the character you typed does.

+

/

+

This is the “What is that on the screen?” command. A + “/” followed by any character that you see on the level, will tell you + what that character is. For instance, typing “/@” will tell you that the + “@” symbol represents you, the player.

+

h, H, ^H

+   +

Move left. You move one space to the left. If you use + upper case “h”, you will continue to move left until you run into + something. This works for all movement commands (e.g. “L” means run in + direction “l”) If you use the “control” “h”, you will continue moving in + the specified direction until you pass something interesting or run into + a wall. You should experiment with this, since it is a very useful + command, but very difficult to describe. This also works for all movement +commands.

+

j

+

Move down.

+

k

+

Move up.

+

l

+

Move right.

+

y

+

Move diagonally up and left.

+

u

+

Move diagonally up and right.

+

b

+

Move diagonally down and left.

+

n

+

Move diagonally down and right.

+

t

+

Throw an object. This is a prefix command. When followed with a + direction it throws an object in the specified direction. (e.g. type +“th” to throw something to the +left.)

+

f

+

Fight until someone dies. When followed with a direction this will force you to fight the creature +in that direction until either you or it bites the big +one.

+

m

+

Move onto something without picking it up. This will move you one + space in the direction you specify and, if there is an object there you + can pick up, it won’t do it.

+

z

+

Zap prefix. Point a staff or wand in a given direction and fire it. + Even non-directional staves must be pointed in some direction to be + used.

+

^

+

Identify trap command. If a trap is on your map and you can’t + remember what type it is, you can get rogue to remind you by getting + next to it and typing “^” followed by the direction that would move +you on top of it.

+

s

+

Search for traps and secret doors. Examine each space immediately + adjacent to you for the existence of a trap or secret door. There is a + large chance that even if there is something there, you won’t find it, + so you might have to search a while before you find something.

+

>

+

Climb down a staircase to the next level. Not surprisingly, this can only be done if you are +standing on staircase.

+

<

+

Climb up a staircase to the level above. This can’t be done without + the Amulet of Yendor +in your possession.

+

.

+

Rest. This is the “do nothing” command. This is good for waiting and + healing.

+

,

+

Pick up something. This picks up whatever you are currently standing on, if you are +standing on anything at all.

+

i

+

Inventory. List what you are carrying in +your pack.

+

I

+

Selective inventory. Tells you what a single item in your pack is.

+

q

+

Quaff one of the potions you are carrying.

+

r

+

Read one of the scrolls in your pack.

+

e

+

Eat food from your pack.

+

w

+

Wield a weapon. Take a weapon out of your pack and carry it for use + in combat, replacing the one you are currently using (if any).

+

W

+

Wear armor. You can only wear one suit of armor at a time. This + takes extra time.

+

T

+

Take armor off. You can’t remove armor that is cursed. This takes + extra time.

+

P

+

Put on a ring. You can wear only two rings at a time (one on each + hand). If you aren’t wearing any rings, this command will ask you which + hand you want to wear it on, otherwise, it will place it on the unused + hand. The program assumes that you wield your sword in your right hand.

+

R

+

Remove a ring. If you are only wearing one ring, this command takes + it off. If you are wearing two, it will ask you which one you wish to + remove,

+

d

+

Drop an object. Take something out of your pack and leave it lying + on the floor. Only one object can occupy each space. You cannot drop a + cursed object at all if you are wielding or wearing it.

+

c

+

Call an object something. If you have a type of object in your pack + which you wish to remember something about, you can use the call command + to give a name to that type of object. This is usually used when you + figure out what a potion, scroll, ring, or staff is after you pick it + up, or when you want to remember which of those swords in your pack you + were wielding.

+

D

+

Print out which things you’ve discovered something about. This + command will ask you what type of thing you are interested in. If you + type the character for a given type of object (e.g. +“!” for potion) it will tell you which kinds of +that type of object you’ve discovered (i.e., figured out what they + are). This command works for +potions, scrolls, rings, and staves and +wands.

+

o

+

Examine and set options. This command is further explained in the + section on options.

+

^R

+

Redraws the screen. Useful if spurious messages or transmission + errors have messed up the display.

+

^P

+

Print last message. Useful when a message disappears before you can + read it. This only repeats the last message that was not a mistyped + command so that you don’t loose anything by accidentally typing the + wrong character instead of ^P.

+

<ESCAPE>

+   + Cancel a command, prefix, or count.
+

!

+

Escape to a shell for some commands.

+

Q

+

Quit. Leave the game.

+

S

+

Save the current game in a file. It will ask you whether you wish to + use the default save file. +Caveat: Rogue won’t let you start up a copy of +a saved game, and it removes the save file as soon as you +start up a restored game. This is to prevent people from +saving a game just before a dangerous position and then +restart- ing it if they die. To restore a saved game, give +the file name as an argument to rogue. As +in

+

% rogue +save_file

+

To restart from the default save file (see below), run

+

% rogue -r

+

v

+

Prints the program version number.

+

)

+

Print the weapon you are currently wielding

+

]

+

Print the armor you are currently wearing

+

=

+

Print the rings you are currently wearing

+

@

+

Reprint the status line on the message line

+ +
+ +

5. Rooms

+

Rooms in the dungeons are either lit or +dark. If you walk into a lit room, the entire room will be +drawn on the screen as soon as you enter. If you walk into a +dark room, it will only be displayed as you explore it. Upon +leaving a room, all monsters inside the room are erased from +the screen. In the darkness you can only see one space in +all directions around you. A corridor is always +dark.

+ +

6. Fighting

+

If you see a monster and you wish to fight +it, just attempt to run into it. Many times a monster you +find will mind its own business unless you attack it. It is +often the case that discretion is the better part of +valor.

+ +

7. Objects you can find

+

When you find something in the dungeon, it +is common to want to pick the object up. This is +accomplished in rogue by walking over the object (unless you +use the “m” prefix, see above). If you are +carrying too many things, the pro- gram will tell you and it +won’t pick up the object, other- wise it will add it +to your pack and tell you what you just picked +up.

+ +

Many of the commands that operate on +objects must prompt you to find out which object you want to +use. If you change your mind and don’t want to do that +command after all, just type an +<ESCAPE> and the command will be +aborted.

+ +

Some objects, like armor and weapons, are +easily dif- ferentiated. Others, like scrolls and potions, +are given labels which vary according to type. During a +game, any two of the same kind of object with the same label +are the same type. However, the labels will vary from game +to game.

+ +

When you use one of these labeled objects, +if its effect is obvious, rogue will remember what it is for +you. If it’s effect isn’t extremely obvious you +will be asked what you want to scribble on it so you will +recognize it later, or you can use the “call” +command (see above).

+ +

7.1. Weapons

+

Some weapons, like arrows, come in bunches, +but most come one at a time. In order to use a weapon, you +must wield it. To fire an arrow out of a bow, you must first +wield the bow, then throw the arrow. You can only wield one +weapon at a time, but you can’t change weapons if the +one you are currently wielding is cursed. The commands to +use weapons are “w” (wield) and “t” +(throw).

+ +

7.2. Armor

+ +

There are various sorts of armor lying around in the dungeon. Some of it is +enchanted, some is cursed, and some is just normal. Different armor types have +different armor protection. The higher the armor protection, the more protection the armor affords against the blows of +monsters. Here is a list of the various armor types and +their normal armor protection:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeProtection
None0
Leather armor2
Studded leather / Ring mail3
Scale mail 4
Chain mail  5
Banded mail / Splint mail6
Plate mail     7
+

 

+

+If a piece of armor is enchanted, its +armor protection will be higher than normal. If a suit of +armor is cursed, its armor protection will be lower, and you +will not be able to remove it. However, not all armor with a +protection that is lower than normal is +cursed. + +

+ +

The commands to use weapons are +“W” (wear) and “T” (take +off).

+ +

+ +7.3. Scrolls

+

Scrolls come with titles in an unknown tongue3. After you read a scroll, it +disappears from your pack. The com mand to use a +scroll is “r” (read).

+ +

+ +7.4. Potions + +

+ +

Potions are labeled by the color of the +liquid inside the flask. They disappear after being quaffed. +The command to use a scroll is “q” +(quaff).

+ +

7.5. Staves and Wands

+

Staves and wands do the same kinds of +things. Staves are identified by a type of wood; wands by a +type of metal or bone. They are generally things you want to +do to some- thing over a long distance, so you must point +them at what you wish to affect to use them. Some staves are +not affected by the direction they are pointed, though. +Staves come with multiple magic charges, the number being +random, and when they are used up, the staff is just a piece +of wood or metal.

+ +

The command to use a wand or staff is +“z” (zap)

+ +

7.6. Rings

+ +

Rings are very useful items, since they are +relatively permanent magic, unlike the usually fleeting +effects of potions, scrolls, and staves. Of course, the bad +rings are also more powerful. Most rings also cause you to +use up food more rapidly, the rate varying with the type of +ring. Rings are differentiated by their stone settings. The +com- mands to use rings are “P” (put on) and +“R” (remove).

+ +

7.7. Food

+ +

Food is necessary to keep you going. If you +go too long without eating you will faint, and eventually +die of starvation. The command to use food is +“e” (eat).

+ +

8. Options

+

Due to variations in personal tastes and +conceptions of the way rogue should do things, there are a +set of options you can set that cause rogue to behave in +various different ways.

+ +

8.1 Setting the options

+ +

There are two ways to set the options. The +first is with the “o” command of rogue; the +second is with the “ROGUEOPTS” environment +variable.

+ +

8.1.1. Using the ‘o’ command

+ +

When you type “o” in rogue, it +clears the screen and displays the current settings for all +the options. It then places the cursor by the value of the +first option and waits for you to type. You can type a +<RETURN> which means to go to the next +option, a “−” which means to go to the +previous option, an <ESCAPE> which +means to return to the game, or you can give the option a +value. For boolean options this merely involves typing +“t” for true or “f” for false. For +string options, type the new value followed by a +<RETURN> .

+ +

8.1.2. Using the ROGUEOPTS variable

+ +

The ROGUEOPTS variable is a string +containing a comma separated list of initial values for the +various options. Boolean variables can be turned on by +listing their name or turned off by putting a +“no” in front of the name. Thus to set up an +environment variable so that jump is on, terse +is off, and the name is set to “Blue +Meanie”, use the command

+ +

% setenv ROGUEOPTS +"jump,noterse,name=Blue Meanie"4

+ +

8.2. Option list

+ +

Here is a list of the options and an +explanation of what each one is for. The default value for +each is enclosed in square brackets. For character string +options, input over fifty characters will be +ignored.

+ +

terse +[noterse]

+
+

+ Useful for those who are tired of the +sometimes lengthy messages of rogue. This is a useful option +for playing on slow terminals, so this option defaults to +terse if you are on a slow (1200 baud or under) +terminal.

+
+ +

jump +[nojump]

+
+

If this option is set, running moves will +not be displayed until you reach the end of the move. This +saves considerable cpu and display time. This option +defaults to jump if you are using a slow +terminal.

+
+

flush +[noflush]

+
+

All typeahead is thrown away after each round of battle. This is useful for those who type far +ahead and then watch in dismay as a Bat kills +them.

+
+

seefloor +[seefloor]

+
+

Display the floor around you on the screen +as you move through dark rooms. Due to the amount of +characters generated, this option defaults to +noseefloor if you are using a slow +terminal.

+
+

passgo +[nopassgo]

+
+

Follow turnings in passageways. If you run +in a pas- sage and you run into stone or a wall, rogue will +see if it can turn to the right or left. If it can only turn +one way, it will turn that way. If it can turn either or +neither, it will stop. This algorithm can sometimes lead to +slightly confusing occurrences which is why it defaults to +nopassgo.

+
+

tombstone +[tombstone]

+
+

Print out the tombstone at the end if you +get killed. This is nice but slow, so you can turn it off if +you like.

+
+

inven +[overwrite]

+
+

Inventory type. This can have one of three +values: overwrite, slow, or clear. With +overwrite the top lines of the map are overwritten +with the list when inventory is requested or when +“Which item do you wish to . . .? ” +questions are answered with a “*”. How- ever, if +the list is longer than a screenful, the screen is cleared. +With slow, lists are displayed one item at a time on +the top of the screen, and with clear, the screen is +cleared, the list is displayed, and then the dungeon level +is re-displayed. Due to speed considerations, clear +is the default for terminals without clear-to-end-of-line +capabilities.

+
+

name [account +name]

+
+

This is the name of your character. It is +used if you get on the top ten scorer’s +list.

+
+

fruit +[slime-mold]

+
+

This should hold the name of a fruit that +you enjoy eating. It is basically a whimsy that rogue uses +in a couple of places.

+
+

file +[~/rogue.save]

+
+

The default file name for saving the game. +If your phone is hung up by accident, rogue will +automatically save the game in this file. The file name may +start with the special character “~” which +expands to be your home directory.

+
+

9. Scoring

+ +

Rogue usually maintains a list of the top +scoring people or scores on your machine. Depending on how +it is set up, it can post either the top scores or the top +players. In the latter case, each account on the machine can +post only one non-winning score on this list. If you score +higher than someone else on this list, or better your previous score on the list, you will be inserted in the proper +place under your current name. How many scores are kept can +also be set up by whoever installs it on your +machine.

+ +

If you quit the game, you get out with all +of your gold intact. If, however, you get killed in the +Dungeons of Doom, your body is forwarded to your +next-of-kin, along with 90% of your gold; ten percent of +your gold is kept by the Dungeons’ wizard as a fee5. This should make you +consider whether you want to take one last hit at that +monster and possibly live, or quit and thus stop with +whatever you have. If you quit, you do get all your gold, +but if you swing and live, you might find +more.

+ +

If you just want to see what the current top players/games list is, you can +type

+

% rogue −s

+ +

10. Acknowledgements

+ +

Rogue was originally conceived of by Glenn +Wichman and Michael Toy. Ken Arnold and Michael Toy then +smoothed out the user interface, and added jillions of new +features. We would like to thank Bob Arnold, Michelle Busch, +Andy Hatcher, Kipp Hickman, Mark Horton, Daniel Jensen, Bill +Joy, Joe Kalash, Steve Maurer, Marty McNary, Jan Miller, and +Scott Nelson for their ideas and assistance; and also the +teeming multitudes who graciously ignored work, school, and +social life to play rogue and send us bugs, complaints, suggestions, and just plain flames. And also +Mom.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
UNIX is a registered trademark of The Open Group
1As opposed to pseudo English sentences.
2A minimum screen size of 24 lines by 80 columns is required. If the + screen is larger, only the 24x80 section +will be used for the map.
3Actually, it's a dialect spoken only by the twenty-seven members of + a tribe in Outer Mongolia, but you're not supposed to know that.
4For those of you who use the Bourne shell sh (1), the commands would be

$ ROGUEOPTS="jump,noterse,name=Blue Meanie"
+$ export ROGUEOPTS

5The Dungeon's wizard is named Wally the Wonder Badger. Invocations + should be accompanied by a sizable donation.
+ + diff --git a/src/cc/rogue/rogue.html.in b/src/cc/rogue/rogue.html.in new file mode 100644 index 000000000..260cb7a4d --- /dev/null +++ b/src/cc/rogue/rogue.html.in @@ -0,0 +1,1060 @@ + + + + + + + + + + +

A Guide to the Dungeons of Doom

+ +

Michael C. Toy
+Kenneth C. R. C. Arnold

+ +

Computer Systems Research +Group
+Department of Electrical Engineering and Computer +Science
+University of California
+Berkeley, California 94720

+ + +

ABSTRACT

+ +
+
+

Rogue is a visual CRT based fantasy game which runs under the + UNIX† timesharing system. This paper describes how + to play rogue, and gives a few hints for those who might otherwise get + lost in the Dungeons of Doom.

+
+
+ +

1. Introduction

+ +

You have just finished your years as a +student at the local fighter’s guild. After much +practice and sweat you have finally completed your training +and are ready to embark upon a perilous adventure. As a test +of your skills, the local guildmasters have sent you into +the Dungeons of Doom. Your task is to return with the Amulet +of Yendor. Your reward for the completion of this task will +be a full membership in the local guild. In addition, you +are allowed to keep all the loot you bring back from the +dungeons.

+ +

In preparation for your journey, you are +given an enchanted mace, a bow, and a quiver of arrows taken +from a dragon’s hoard in the far off Dark Mountains. +You are also outfitted with elf-crafted armor and given +enough food to reach the dungeons. You say goodbye to family +and friends for what may be the last time and head up the +road.

+ +

You set out on your way to the dungeons and +after several days of uneventful travel, you see the +ancient ruins that mark the entrance to the Dungeons of +Doom. It is late at night, so you make camp at the entrance +and spend the night sleeping under the open skies. In the +morning you gather your weapons, put on your armor, eat what +is almost your last food, and enter the +dungeons.

+ +

2. What is going on here?

+

You have just begun a game of rogue. Your +goal is to grab as much treasure as you can, find the Amulet +of Yendor, and get out of the Dungeons of Doom alive. On the +screen, a map of where you have been and what you have seen +on the current dungeon level is kept. As you explore more of +the level, it appears on the screen in front of +you.

+

Rogue differs from most computer fantasy +games in that it is screen oriented. Commands are all one or +two keystrokes1 and the +results of your commands are displayed graphically on the +screen rather than being explained in words2.

+

Another major difference between rogue and other computer fantasy games is that once you have solved +all the puzzles in a standard fantasy game, it has lost most +of its excitement and it ceases to be fun. Rogue, on the +other hand, generates a new dungeon every time you play it +and even the author finds it an entertaining and exciting +game.

+ +

3. What do all those things on the screen mean?

+

In order to understand what is going on in +rogue you have to first get some grasp of what rogue is +doing with the screen. The rogue screen is intended to +replace the “You can see ...” descriptions of +standard fantasy games. Figure 1 is a sample of what a +rogue screen might look like.

+ +
+ +
+
+____________________________________________________________
+
+
+                        ------------
+                        |..........+
+                        |..@....]..|
+                        |....B.....|
+                        |..........|
+                        -----+------
+
+
+
+Level: 1  Gold: 0      Hp: 12(12)  Str: 16(16)  Arm: 4  Exp: 1/0
+
+                          Figure 1
+____________________________________________________________
+
+
+
+

3.1. The bottom line

+

At the bottom line of the screen are a few +pieces of cryptic information describing your current +status. Here is an explanation of what these things +mean:

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Level

+

This number indicates how deep you +have gone in the dungeon. It starts at one and goes up as +you go deeper into the dungeon.

+

Gold

+

The number of gold pieces you have managed to find and keep with you + so far.

+

Hp

+

Your current and maximum health points. +Health points indicate how much damage you can take before +you die. The more you get hit in a fight, the lower they +get. You can regain health points by resting. The number in +parentheses is the maximum number your health points can +reach.

+ +
+

Str

+ +

Your current strength and maximum ever +strength. This can be any integer less than or equal to 31, +or greater than or equal to three. The higher the num- ber, +the stronger you are. The number in the parentheses is the +maximum strength you have attained so far this +game.

+ +
+

Arm

+

Your current armor protection. This +number indicates how effective your armor is in stopping +blows from unfriendly creatures. The higher this number is, +the more effective the armor.

+ +
+

Exp

+

These two numbers give your current +experience level and experience points. As you do things, +you gain experience points. At certain experience point +totals, you gain an experience level. The more experienced +you are, the better you are able to fight and to withstand +magical attacks.

+ +
+ +

 

+ +

3.2. The top line

+

The top line of the screen is reserved for +printing messages that describe things that are impossible +to represent visually. If you see a “--More--” +on the top line, this means that rogue wants to print +another message on the screen, but it wants to make certain +that you have read the one that is there first. To read the +next message, just type a space.

+ + +

3.3. The rest of the screen

+

The rest of the screen is the map of the +level as you have explored it so far. Each symbol on the +screen repre- sents something. Here is a list of what the +various symbols mean:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

@

+

This symbol represents you, the adventurer.

+

- |

+

These symbols represent the walls of rooms.

+

+

+

A door to/from a room.

+

.

+

The floor of a room.

+

#

+

The floor of a passage between rooms.

+

*

+

A pile or pot of gold.

+

)

+

A weapon of some sort.

+

]

+

A piece of armor.

+

!

+

A flask containing a magic potion.

+

?

+

A piece of paper, usually a magic scroll.

+

=

+

A ring with magic properties

+

/

+

A magical staff or wand

+

^

+

A trap, watch out for these.

+

%

+

A staircase to other levels

+

:

+

A piece of food.

+

A-Z

+

The uppercase letters represent the various + inhabitants of the Dungeons of Doom. Watch out, they can be nasty and + vicious.

+ + +

4. Commands

+

Commands are given to rogue by typing one or two characters. +Most commands can be preceded by a count to repeat them (e.g. typing “10s” will +do ten searches). Commands for which counts make no sense have the count +ignored. To cancel a count or a prefix, type <ESCAPE> . The list of commands is +rather long, but it can be read at any time during the game +with the “?” command. Here it is for reference, +with a short explanation of each +command.

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

?

+

The help command. Asks for a character to give help + on. If you type a “*”, it will list all the commands, otherwise it will + explain what the character you typed does.

+

/

+

This is the “What is that on the screen?” command. A + “/” followed by any character that you see on the level, will tell you + what that character is. For instance, typing “/@” will tell you that the + “@” symbol represents you, the player.

+

h, H, ^H

+   +

Move left. You move one space to the left. If you use + upper case “h”, you will continue to move left until you run into + something. This works for all movement commands (e.g. “L” means run in + direction “l”) If you use the “control” “h”, you will continue moving in + the specified direction until you pass something interesting or run into + a wall. You should experiment with this, since it is a very useful + command, but very difficult to describe. This also works for all movement +commands.

+

j

+

Move down.

+

k

+

Move up.

+

l

+

Move right.

+

y

+

Move diagonally up and left.

+

u

+

Move diagonally up and right.

+

b

+

Move diagonally down and left.

+

n

+

Move diagonally down and right.

+

t

+

Throw an object. This is a prefix command. When followed with a + direction it throws an object in the specified direction. (e.g. type +“th” to throw something to the +left.)

+

f

+

Fight until someone dies. When followed with a direction this will force you to fight the creature +in that direction until either you or it bites the big +one.

+

m

+

Move onto something without picking it up. This will move you one + space in the direction you specify and, if there is an object there you + can pick up, it won’t do it.

+

z

+

Zap prefix. Point a staff or wand in a given direction and fire it. + Even non-directional staves must be pointed in some direction to be + used.

+

^

+

Identify trap command. If a trap is on your map and you can’t + remember what type it is, you can get rogue to remind you by getting + next to it and typing “^” followed by the direction that would move +you on top of it.

+

s

+

Search for traps and secret doors. Examine each space immediately + adjacent to you for the existence of a trap or secret door. There is a + large chance that even if there is something there, you won’t find it, + so you might have to search a while before you find something.

+

>

+

Climb down a staircase to the next level. Not surprisingly, this can only be done if you are +standing on staircase.

+

<

+

Climb up a staircase to the level above. This can’t be done without + the Amulet of Yendor +in your possession.

+

.

+

Rest. This is the “do nothing” command. This is good for waiting and + healing.

+

,

+

Pick up something. This picks up whatever you are currently standing on, if you are +standing on anything at all.

+

i

+

Inventory. List what you are carrying in +your pack.

+

I

+

Selective inventory. Tells you what a single item in your pack is.

+

q

+

Quaff one of the potions you are carrying.

+

r

+

Read one of the scrolls in your pack.

+

e

+

Eat food from your pack.

+

w

+

Wield a weapon. Take a weapon out of your pack and carry it for use + in combat, replacing the one you are currently using (if any).

+

W

+

Wear armor. You can only wear one suit of armor at a time. This + takes extra time.

+

T

+

Take armor off. You can’t remove armor that is cursed. This takes + extra time.

+

P

+

Put on a ring. You can wear only two rings at a time (one on each + hand). If you aren’t wearing any rings, this command will ask you which + hand you want to wear it on, otherwise, it will place it on the unused + hand. The program assumes that you wield your sword in your right hand.

+

R

+

Remove a ring. If you are only wearing one ring, this command takes + it off. If you are wearing two, it will ask you which one you wish to + remove,

+

d

+

Drop an object. Take something out of your pack and leave it lying + on the floor. Only one object can occupy each space. You cannot drop a + cursed object at all if you are wielding or wearing it.

+

c

+

Call an object something. If you have a type of object in your pack + which you wish to remember something about, you can use the call command + to give a name to that type of object. This is usually used when you + figure out what a potion, scroll, ring, or staff is after you pick it + up, or when you want to remember which of those swords in your pack you + were wielding.

+

D

+

Print out which things you’ve discovered something about. This + command will ask you what type of thing you are interested in. If you + type the character for a given type of object (e.g. +“!” for potion) it will tell you which kinds of +that type of object you’ve discovered (i.e., figured out what they + are). This command works for +potions, scrolls, rings, and staves and +wands.

+

o

+

Examine and set options. This command is further explained in the + section on options.

+

^R

+

Redraws the screen. Useful if spurious messages or transmission + errors have messed up the display.

+

^P

+

Print last message. Useful when a message disappears before you can + read it. This only repeats the last message that was not a mistyped + command so that you don’t loose anything by accidentally typing the + wrong character instead of ^P.

+

<ESCAPE>

+   + Cancel a command, prefix, or count.
+

!

+

Escape to a shell for some commands.

+

Q

+

Quit. Leave the game.

+

S

+

Save the current game in a file. It will ask you whether you wish to + use the default save file. +Caveat: Rogue won’t let you start up a copy of +a saved game, and it removes the save file as soon as you +start up a restored game. This is to prevent people from +saving a game just before a dangerous position and then +restart- ing it if they die. To restore a saved game, give +the file name as an argument to rogue. As +in

+

% rogue +save_file

+

To restart from the default save file (see below), run

+

% rogue -r

+

v

+

Prints the program version number.

+

)

+

Print the weapon you are currently wielding

+

]

+

Print the armor you are currently wearing

+

=

+

Print the rings you are currently wearing

+

@

+

Reprint the status line on the message line

+ +
+ +

5. Rooms

+

Rooms in the dungeons are either lit or +dark. If you walk into a lit room, the entire room will be +drawn on the screen as soon as you enter. If you walk into a +dark room, it will only be displayed as you explore it. Upon +leaving a room, all monsters inside the room are erased from +the screen. In the darkness you can only see one space in +all directions around you. A corridor is always +dark.

+ +

6. Fighting

+

If you see a monster and you wish to fight +it, just attempt to run into it. Many times a monster you +find will mind its own business unless you attack it. It is +often the case that discretion is the better part of +valor.

+ +

7. Objects you can find

+

When you find something in the dungeon, it +is common to want to pick the object up. This is +accomplished in rogue by walking over the object (unless you +use the “m” prefix, see above). If you are +carrying too many things, the pro- gram will tell you and it +won’t pick up the object, other- wise it will add it +to your pack and tell you what you just picked +up.

+ +

Many of the commands that operate on +objects must prompt you to find out which object you want to +use. If you change your mind and don’t want to do that +command after all, just type an +<ESCAPE> and the command will be +aborted.

+ +

Some objects, like armor and weapons, are +easily dif- ferentiated. Others, like scrolls and potions, +are given labels which vary according to type. During a +game, any two of the same kind of object with the same label +are the same type. However, the labels will vary from game +to game.

+ +

When you use one of these labeled objects, +if its effect is obvious, rogue will remember what it is for +you. If it’s effect isn’t extremely obvious you +will be asked what you want to scribble on it so you will +recognize it later, or you can use the “call” +command (see above).

+ +

7.1. Weapons

+

Some weapons, like arrows, come in bunches, +but most come one at a time. In order to use a weapon, you +must wield it. To fire an arrow out of a bow, you must first +wield the bow, then throw the arrow. You can only wield one +weapon at a time, but you can’t change weapons if the +one you are currently wielding is cursed. The commands to +use weapons are “w” (wield) and “t” +(throw).

+ +

7.2. Armor

+ +

There are various sorts of armor lying around in the dungeon. Some of it is +enchanted, some is cursed, and some is just normal. Different armor types have +different armor protection. The higher the armor protection, the more protection the armor affords against the blows of +monsters. Here is a list of the various armor types and +their normal armor protection:

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeProtection
None0
Leather armor2
Studded leather / Ring mail3
Scale mail 4
Chain mail  5
Banded mail / Splint mail6
Plate mail     7
+

 

+

+If a piece of armor is enchanted, its +armor protection will be higher than normal. If a suit of +armor is cursed, its armor protection will be lower, and you +will not be able to remove it. However, not all armor with a +protection that is lower than normal is +cursed. + +

+ +

The commands to use weapons are +“W” (wear) and “T” (take +off).

+ +

+ +7.3. Scrolls

+

Scrolls come with titles in an unknown tongue3. After you read a scroll, it +disappears from your pack. The com mand to use a +scroll is “r” (read).

+ +

+ +7.4. Potions + +

+ +

Potions are labeled by the color of the +liquid inside the flask. They disappear after being quaffed. +The command to use a scroll is “q” +(quaff).

+ +

7.5. Staves and Wands

+

Staves and wands do the same kinds of +things. Staves are identified by a type of wood; wands by a +type of metal or bone. They are generally things you want to +do to some- thing over a long distance, so you must point +them at what you wish to affect to use them. Some staves are +not affected by the direction they are pointed, though. +Staves come with multiple magic charges, the number being +random, and when they are used up, the staff is just a piece +of wood or metal.

+ +

The command to use a wand or staff is +“z” (zap)

+ +

7.6. Rings

+ +

Rings are very useful items, since they are +relatively permanent magic, unlike the usually fleeting +effects of potions, scrolls, and staves. Of course, the bad +rings are also more powerful. Most rings also cause you to +use up food more rapidly, the rate varying with the type of +ring. Rings are differentiated by their stone settings. The +com- mands to use rings are “P” (put on) and +“R” (remove).

+ +

7.7. Food

+ +

Food is necessary to keep you going. If you +go too long without eating you will faint, and eventually +die of starvation. The command to use food is +“e” (eat).

+ +

8. Options

+

Due to variations in personal tastes and +conceptions of the way rogue should do things, there are a +set of options you can set that cause rogue to behave in +various different ways.

+ +

8.1 Setting the options

+ +

There are two ways to set the options. The +first is with the “o” command of rogue; the +second is with the “ROGUEOPTS” environment +variable.

+ +

8.1.1. Using the ‘o’ command

+ +

When you type “o” in rogue, it +clears the screen and displays the current settings for all +the options. It then places the cursor by the value of the +first option and waits for you to type. You can type a +<RETURN> which means to go to the next +option, a “−” which means to go to the +previous option, an <ESCAPE> which +means to return to the game, or you can give the option a +value. For boolean options this merely involves typing +“t” for true or “f” for false. For +string options, type the new value followed by a +<RETURN> .

+ +

8.1.2. Using the ROGUEOPTS variable

+ +

The ROGUEOPTS variable is a string +containing a comma separated list of initial values for the +various options. Boolean variables can be turned on by +listing their name or turned off by putting a +“no” in front of the name. Thus to set up an +environment variable so that jump is on, terse +is off, and the name is set to “Blue +Meanie”, use the command

+ +

% setenv ROGUEOPTS +"jump,noterse,name=Blue Meanie"4

+ +

8.2. Option list

+ +

Here is a list of the options and an +explanation of what each one is for. The default value for +each is enclosed in square brackets. For character string +options, input over fifty characters will be +ignored.

+ +

terse +[noterse]

+
+

+ Useful for those who are tired of the +sometimes lengthy messages of rogue. This is a useful option +for playing on slow terminals, so this option defaults to +terse if you are on a slow (1200 baud or under) +terminal.

+
+ +

jump +[nojump]

+
+

If this option is set, running moves will +not be displayed until you reach the end of the move. This +saves considerable cpu and display time. This option +defaults to jump if you are using a slow +terminal.

+
+

flush +[noflush]

+
+

All typeahead is thrown away after each round of battle. This is useful for those who type far +ahead and then watch in dismay as a Bat kills +them.

+
+

seefloor +[seefloor]

+
+

Display the floor around you on the screen +as you move through dark rooms. Due to the amount of +characters generated, this option defaults to +noseefloor if you are using a slow +terminal.

+
+

passgo +[nopassgo]

+
+

Follow turnings in passageways. If you run +in a pas- sage and you run into stone or a wall, rogue will +see if it can turn to the right or left. If it can only turn +one way, it will turn that way. If it can turn either or +neither, it will stop. This algorithm can sometimes lead to +slightly confusing occurrences which is why it defaults to +nopassgo.

+
+

tombstone +[tombstone]

+
+

Print out the tombstone at the end if you +get killed. This is nice but slow, so you can turn it off if +you like.

+
+

inven +[overwrite]

+
+

Inventory type. This can have one of three +values: overwrite, slow, or clear. With +overwrite the top lines of the map are overwritten +with the list when inventory is requested or when +“Which item do you wish to . . .? ” +questions are answered with a “*”. How- ever, if +the list is longer than a screenful, the screen is cleared. +With slow, lists are displayed one item at a time on +the top of the screen, and with clear, the screen is +cleared, the list is displayed, and then the dungeon level +is re-displayed. Due to speed considerations, clear +is the default for terminals without clear-to-end-of-line +capabilities.

+
+

name [account +name]

+
+

This is the name of your character. It is +used if you get on the top ten scorer’s +list.

+
+

fruit +[slime-mold]

+
+

This should hold the name of a fruit that +you enjoy eating. It is basically a whimsy that rogue uses +in a couple of places.

+
+

file +[~/rogue.save]

+
+

The default file name for saving the game. +If your phone is hung up by accident, rogue will +automatically save the game in this file. The file name may +start with the special character “~” which +expands to be your home directory.

+
+

9. Scoring

+ +

Rogue usually maintains a list of the top +scoring people or scores on your machine. Depending on how +it is set up, it can post either the top scores or the top +players. In the latter case, each account on the machine can +post only one non-winning score on this list. If you score +higher than someone else on this list, or better your previous score on the list, you will be inserted in the proper +place under your current name. How many scores are kept can +also be set up by whoever installs it on your +machine.

+ +

If you quit the game, you get out with all +of your gold intact. If, however, you get killed in the +Dungeons of Doom, your body is forwarded to your +next-of-kin, along with 90% of your gold; ten percent of +your gold is kept by the Dungeons’ wizard as a fee5. This should make you +consider whether you want to take one last hit at that +monster and possibly live, or quit and thus stop with +whatever you have. If you quit, you do get all your gold, +but if you swing and live, you might find +more.

+ +

If you just want to see what the current top players/games list is, you can +type

+

% @PROGRAM@ −s

+ +

10. Acknowledgements

+ +

Rogue was originally conceived of by Glenn +Wichman and Michael Toy. Ken Arnold and Michael Toy then +smoothed out the user interface, and added jillions of new +features. We would like to thank Bob Arnold, Michelle Busch, +Andy Hatcher, Kipp Hickman, Mark Horton, Daniel Jensen, Bill +Joy, Joe Kalash, Steve Maurer, Marty McNary, Jan Miller, and +Scott Nelson for their ideas and assistance; and also the +teeming multitudes who graciously ignored work, school, and +social life to play rogue and send us bugs, complaints, suggestions, and just plain flames. And also +Mom.

+ + + + + + + + + + + + + + + + + + + + + + + + + +
UNIX is a registered trademark of The Open Group
1As opposed to pseudo English sentences.
2A minimum screen size of 24 lines by 80 columns is required. If the + screen is larger, only the 24x80 section +will be used for the map.
3Actually, it's a dialect spoken only by the twenty-seven members of + a tribe in Outer Mongolia, but you're not supposed to know that.
4For those of you who use the Bourne shell sh (1), the commands would be

$ ROGUEOPTS="jump,noterse,name=Blue Meanie"
+$ export ROGUEOPTS

5The Dungeon's wizard is named Wally the Wonder Badger. Invocations + should be accompanied by a sizable donation.
+ + diff --git a/src/cc/rogue/rogue.me b/src/cc/rogue/rogue.me new file mode 100644 index 000000000..1a2568477 --- /dev/null +++ b/src/cc/rogue/rogue.me @@ -0,0 +1,892 @@ +.\" +.\" @(#)rogue.me 6.2 (Berkeley) 4/28/86 +.\" +.\" Rogue: Exploring the Dungeons of Doom +.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman +.\" All rights reserved. +.\" +.\" See the file LICENSE.TXT for full copyright and licensing information. +.\" +.ds E \s-2\s0 +.ds R \s-2\s0 +.ds U \s-2UNIX\s0 +.ie t .ds _ \d\(mi\u +.el .ds _ _ +.de Cs +\&\\$3\*(lq\\$1\*(rq\\$2 +.. +.sp 5 +.ce 1000 +.ps +4 +.vs +4p +.b +A Guide to the Dungeons of Doom +.r +.vs +.ps +.sp 2 +.i +Michael C. Toy +Kenneth C. R. C. Arnold +.r +.sp 2 +Computer Systems Research Group +Department of Electrical Engineering and Computer Science +University of California +Berkeley, California 94720 +.sp 4 +.i ABSTRACT +.ce 0 +.(b I F +.bi Rogue +is a visual CRT based fantasy game +which runs under the \*U\(dg timesharing system. +.(f +\fR\(dg\*U is a trademark of Bell Laboratories\fP +.)f +This paper describes how to play rogue, +and gives a few hints +for those who might otherwise get lost in the Dungeons of Doom. +.)b +\".he '''\fBA Guide to the Dungeons of Doom\fP' +\" .fo ''- % -'' +.eh 'USD:33-%''A Guide to the Dungeons of Doom' +.oh 'A Guide to the Dungeons of Doom''USD:33-%' +.sh 1 Introduction +.pp +You have just finished your years as a student at the local fighter's guild. +After much practice and sweat you have finally completed your training +and are ready to embark upon a perilous adventure. +As a test of your skills, +the local guildmasters have sent you into the Dungeons of Doom. +Your task is to return with the Amulet of Yendor. +Your reward for the completion of this task +will be a full membership in the local guild. +In addition, +you are allowed to keep all the loot you bring back from the dungeons. +.pp +In preparation for your journey, +you are given an enchanted mace, +a bow, and a quiver of arrows +taken from a dragon's hoard in the far off Dark Mountains. +You are also outfitted with elf-crafted armor +and given enough food to reach the dungeons. +You say goodbye to family and friends for what may be the last time +and head up the road. +.pp +You set out on your way to the dungeons +and after several days of uneventful travel, +you see the ancient ruins +that mark the entrance to the Dungeons of Doom. +It is late at night, +so you make camp at the entrance +and spend the night sleeping under the open skies. +In the morning you gather your weapons, +put on your armor, +eat what is almost your last food, +and enter the dungeons. +.sh 1 "What is going on here?" +.pp +You have just begun a game of rogue. +Your goal is to grab as much treasure as you can, +find the Amulet of Yendor, +and get out of the Dungeons of Doom alive. +On the screen, +a map of where you have been +and what you have seen on the current dungeon level is kept. +As you explore more of the level, +it appears on the screen in front of you. +.pp +Rogue differs from most computer fantasy games in that it is screen oriented. +Commands are all one or two keystrokes\** +.(f +\** As opposed to pseudo English sentences. +.)f +and the results of your commands +are displayed graphically on the screen rather +than being explained in words.\** +.(f +\** A minimum screen size of 24 lines by 80 columns is required. +If the screen is larger, only the 24x80 section will be used +for the map. +.)f +.pp +Another major difference between rogue and other computer fantasy games +is that once you have solved all the puzzles in a standard fantasy game, +it has lost most of its excitement and it ceases to be fun. +Rogue, +on the other hand, +generates a new dungeon every time you play it +and even the author finds it an entertaining and exciting game. +.sh 1 "What do all those things on the screen mean?" +.pp +In order to understand what is going on in rogue +you have to first get some grasp of what rogue is doing with the screen. +The rogue screen is intended +to replace the \*(lqYou can see ...\*(rq descriptions +of standard fantasy games. +Figure 1 is a sample of what a rogue screen might look like. +.(z +.hl +.nf +.TS +center; +ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce. +- - - - - - - - - - - - +| . . . . . . . . . . + +| . . @ . . . . ] . . | +| . . . . B . . . . . | +| . . . . . . . . . . | +- - - - - + - - - - - - +.TE + + +.ce 1000 +Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0 + +Figure 1 +.ce +.hl +.)z +.sh 2 "The bottom line" +.pp +At the bottom line of the screen +are a few pieces of cryptic information +describing your current status. +Here is an explanation of what these things mean: +.ip Level \w'Level\ \ 'u +This number indicates how deep you have gone in the dungeon. +It starts at one and goes up as you go deeper into the dungeon. +.ip Gold \w'Level\ \ 'u +The number of gold pieces you have managed to find +and keep with you so far. +.ip Hp \w'Level\ \ 'u +Your current and maximum health points. +Health points indicate how much damage you can take before you die. +The more you get hit in a fight, +the lower they get. +You can regain health points by resting. +The number in parentheses +is the maximum number your health points can reach. +.ip Str \w'Level\ \ 'u +Your current strength and maximum ever strength. +This can be any integer less than or equal to 31, +or greater than or equal to three. +The higher the number, +the stronger you are. +The number in the parentheses +is the maximum strength you have attained so far this game. +.ip Arm \w'Level\ \ 'u +Your current armor protection. +This number indicates how effective your armor is +in stopping blows from unfriendly creatures. +The higher this number is, +the more effective the armor. +.ip Exp \w'Level\ \ 'u +These two numbers give your current experience level +and experience points. +As you do things, +you gain experience points. +At certain experience point totals, +you gain an experience level. +The more experienced you are, +the better you are able to fight and to withstand magical attacks. +.sh 2 "The top line" +.pp +The top line of the screen is reserved +for printing messages that describe things +that are impossible to represent visually. +If you see a \*(lq--More--\*(rq on the top line, +this means that rogue wants to print another message on the screen, +but it wants to make certain +that you have read the one that is there first. +To read the next message, +just type a space. +.sh 2 "The rest of the screen" +.pp +The rest of the screen is the map of the level +as you have explored it so far. +Each symbol on the screen represents something. +Here is a list of what the various symbols mean: +.ip @ +This symbol represents you, the adventurer. +.ip "-\^|" +These symbols represent the walls of rooms. +.ip + +A door to/from a room. +.ip . +The floor of a room. +.ip # +The floor of a passage between rooms. +.ip * +A pile or pot of gold. +.ip ) +A weapon of some sort. +.ip ] +A piece of armor. +.ip ! +A flask containing a magic potion. +.ip ? +A piece of paper, usually a magic scroll. +.ip = +A ring with magic properties +.ip / +A magical staff or wand +.ip ^ +A trap, watch out for these. +.ip % +A staircase to other levels +.ip : +A piece of food. +.ip A-Z +The uppercase letters +represent the various inhabitants of the Dungeons of Doom. +Watch out, they can be nasty and vicious. +.sh 1 Commands +.pp +Commands are given to rogue by typing one or two characters. +Most commands can be preceded by a count to repeat them +(e.g. typing +.Cs 10s +will do ten searches). +Commands for which counts make no sense +have the count ignored. +To cancel a count or a prefix, +type \*E. +The list of commands is rather long, +but it can be read at any time during the game with the +.Cs ? +command. +Here it is for reference, +with a short explanation of each command. +.ip ? +The help command. +Asks for a character to give help on. +If you type a +.Cs * , +it will list all the commands, +otherwise it will explain what the character you typed does. +.ip / +This is the \*(lqWhat is that on the screen?\*(rq command. +A +.Cs / +followed by any character that you see on the level, +will tell you what that character is. +For instance, +typing +.Cs /@ +will tell you that the +.Cs @ +symbol represents you, the player. +.ip "h, H, ^H" +Move left. +You move one space to the left. +If you use upper case +.Cs h , +you will continue to move left until you run into something. +This works for all movement commands +(e.g. +.Cs L +means run in direction +.Cs l ) +If you use the \*(lqcontrol\*(rq +.Cs h , +you will continue moving in the specified direction +until you pass something interesting or run into a wall. +You should experiment with this, +since it is a very useful command, +but very difficult to describe. +This also works for all movement commands. +.ip j +Move down. +.ip k +Move up. +.ip l +Move right. +.ip y +Move diagonally up and left. +.ip u +Move diagonally up and right. +.ip b +Move diagonally down and left. +.ip n +Move diagonally down and right. +.ip t +Throw an object. +This is a prefix command. +When followed with a direction +it throws an object in the specified direction. +(e.g. type +.Cs th +to throw +something to the left.) +.ip f +Fight until someone dies. +When followed with a direction +this will force you to fight the creature in that direction +until either you or it bites the big one. +.ip m +Move onto something without picking it up. +This will move you one space in the direction you specify and, +if there is an object there you can pick up, +it won't do it. +.ip z +Zap prefix. +Point a staff or wand in a given direction +and fire it. +Even non-directional staves must be pointed in some direction +to be used. +.ip ^ +Identify trap command. +If a trap is on your map +and you can't remember what type it is, +you can get rogue to remind you +by getting next to it and typing +.Cs ^ +followed by the direction that would move you on top of it. +.ip s +Search for traps and secret doors. +Examine each space immediately adjacent to you +for the existence of a trap or secret door. +There is a large chance that even if there is something there, +you won't find it, +so you might have to search a while before you find something. +.ip > +Climb down a staircase to the next level. +Not surprisingly, this can only be done if you are standing on staircase. +.ip < +Climb up a staircase to the level above. +This can't be done without the Amulet of Yendor in your possession. +.ip "." +Rest. +This is the \*(lqdo nothing\*(rq command. +This is good for waiting and healing. +.ip , +Pick up something. +This picks up whatever you are currently standing on, +if you are standing on anything at all. +.ip i +Inventory. +List what you are carrying in your pack. +.ip I +Selective inventory. +Tells you what a single item in your pack is. +.ip q +Quaff one of the potions you are carrying. +.ip r +Read one of the scrolls in your pack. +.ip e +Eat food from your pack. +.ip w +Wield a weapon. +Take a weapon out of your pack and carry it for use in combat, +replacing the one you are currently using (if any). +.ip W +Wear armor. +You can only wear one suit of armor at a time. +This takes extra time. +.ip T +Take armor off. +You can't remove armor that is cursed. +This takes extra time. +.ip P +Put on a ring. +You can wear only two rings at a time +(one on each hand). +If you aren't wearing any rings, +this command will ask you which hand you want to wear it on, +otherwise, it will place it on the unused hand. +The program assumes that you wield your sword in your right hand. +.ip R +Remove a ring. +If you are only wearing one ring, +this command takes it off. +If you are wearing two, +it will ask you which one you wish to remove, +.ip d +Drop an object. +Take something out of your pack and leave it lying on the floor. +Only one object can occupy each space. +You cannot drop a cursed object at all +if you are wielding or wearing it. +.ip c +Call an object something. +If you have a type of object in your pack +which you wish to remember something about, +you can use the call command to give a name to that type of object. +This is usually used when you figure out what a +potion, scroll, ring, or staff is +after you pick it up, +or when you want to remember +which of those swords in your pack you were wielding. +.ip D +Print out which things you've discovered something about. +This command will ask you what type of thing you are interested in. +If you type the character for a given type of object +(\fIe.g.\fP +.Cs ! +for potion) +it will tell you which kinds of that type of object you've discovered +(\fIi.e.\fP, figured out what they are). +This command works for potions, scrolls, rings, and staves and wands. +.ip o +Examine and set options. +This command is further explained in the section on options. +.ip ^R +Redraws the screen. +Useful if spurious messages or transmission errors +have messed up the display. +.ip ^P +Print last message. +Useful when a message disappears before you can read it. +This only repeats the last message +that was not a mistyped command +so that you don't loose anything by accidentally typing +the wrong character instead of ^P. +.ip \*E +Cancel a command, prefix, or count. +.ip ! +Escape to a shell for some commands. +.ip Q +Quit. +Leave the game. +.ip S +Save the current game in a file. +It will ask you whether you wish to use the default save file. +.i Caveat : +Rogue won't let you start up a copy of a saved game, +and it removes the save file as soon as you start up a restored game. +This is to prevent people from saving a game just before a dangerous position +and then restarting it if they die. +To restore a saved game, +give the file name as an argument to rogue. +As in +.ti +1i +.nf +% rogue \fIsave\*_file\fP +.ip +To restart from the default save file (see below), +run +.ti +1i +.nf +% rogue \-r +.ip v +Prints the program version number. +.ip ) +Print the weapon you are currently wielding +.ip ] +Print the armor you are currently wearing +.ip = +Print the rings you are currently wearing +.ip @ +Reprint the status line on the message line +.sh 1 Rooms +.pp +Rooms in the dungeons are either lit or dark. +If you walk into a lit room, +the entire room will be drawn on the screen as soon as you enter. +If you walk into a dark room, +it will only be displayed as you explore it. +Upon leaving a room, +all monsters inside the room +are erased from the screen. +In the darkness you can only see one space +in all directions around you. +A corridor is always dark. +.sh 1 Fighting +.pp +If you see a monster and you wish to fight it, +just attempt to run into it. +Many times a monster you find will mind its own business +unless you attack it. +It is often the case that discretion is the better part of valor. +.sh 1 "Objects you can find" +.pp +When you find something in the dungeon, +it is common to want to pick the object up. +This is accomplished in rogue by walking over the object +(unless you use the +.Cs m +prefix, see above). +If you are carrying too many things, +the program will tell you and it won't pick up the object, +otherwise it will add it to your pack +and tell you what you just picked up. +.pp +Many of the commands that operate on objects must prompt you +to find out which object you want to use. +If you change your mind and don't want to do that command after all, +just type an \*E and the command will be aborted. +.pp +Some objects, like armor and weapons, +are easily differentiated. +Others, like scrolls and potions, +are given labels which vary according to type. +During a game, +any two of the same kind of object +with the same label +are the same type. +However, +the labels will vary from game to game. +.pp +When you use one of these labeled objects, +if its effect is obvious, +rogue will remember what it is for you. +If it's effect isn't extremely obvious +you will be asked what you want to scribble on it +so you will recognize it later, +or you can use the +.Cs call +command +(see above). +.sh 2 Weapons +.pp +Some weapons, +like arrows, +come in bunches, +but most come one at a time. +In order to use a weapon, +you must wield it. +To fire an arrow out of a bow, +you must first wield the bow, +then throw the arrow. +You can only wield one weapon at a time, +but you can't change weapons if the one +you are currently wielding is cursed. +The commands to use weapons are +.Cs w +(wield) +and +.Cs t +(throw). +.sh 2 Armor +.pp +There are various sorts of armor lying around in the dungeon. +Some of it is enchanted, +some is cursed, +and some is just normal. +Different armor types have different armor protection. +The higher the armor protection, +the more protection the armor affords against the blows of monsters. +Here is a list of the various armor types and their normal armor protection: +.(b +.TS +box center; +l r. +\ \ \fIType Protection\fP +None 0 +Leather armor 2 +Studded leather / Ring mail 3 +Scale mail 4 +Chain mail 5 +Banded mail / Splint mail 6 +Plate mail 7 +.TE +.)b +.lp +If a piece of armor is enchanted, +its armor protection will be higher than normal. +If a suit of armor is cursed, +its armor protection will be lower, +and you will not be able to remove it. +However, not all armor with a protection that is lower than normal is cursed. +.pp +The commands to use weapons are +.Cs W +(wear) +and +.Cs T +(take off). +.sh 2 Scrolls +.pp +Scrolls come with titles in an unknown tongue\**. +.(f +\** Actually, it's a dialect spoken only by the twenty-seven members +of a tribe in Outer Mongolia, +but you're not supposed to +.i know +that. +.)f +After you read a scroll, +it disappears from your pack. +The command to use a scroll is +.Cs r +(read). +.sh 2 Potions +.pp +Potions are labeled by the color of the liquid inside the flask. +They disappear after being quaffed. +The command to use a scroll is +.Cs q +(quaff). +.sh 2 "Staves and Wands" +.pp +Staves and wands do the same kinds of things. +Staves are identified by a type of wood; +wands by a type of metal or bone. +They are generally things you want to do to something +over a long distance, +so you must point them at what you wish to affect +to use them. +Some staves are not affected by the direction they are pointed, though. +Staves come with multiple magic charges, +the number being random, +and when they are used up, +the staff is just a piece of wood or metal. +.pp +The command to use a wand or staff is +.Cs z +(zap) +.sh 2 Rings +.pp +Rings are very useful items, +since they are relatively permanent magic, +unlike the usually fleeting effects of potions, scrolls, and staves. +Of course, +the bad rings are also more powerful. +Most rings also cause you to use up food more rapidly, +the rate varying with the type of ring. +Rings are differentiated by their stone settings. +The commands to use rings are +.Cs P +(put on) +and +.Cs R +(remove). +.sh 2 Food +.pp +Food is necessary to keep you going. +If you go too long without eating you will faint, +and eventually die of starvation. +The command to use food is +.Cs e +(eat). +.sh 1 Options +.pp +Due to variations in personal tastes +and conceptions of the way rogue should do things, +there are a set of options you can set +that cause rogue to behave in various different ways. +.sh 2 "Setting the options" +.pp +There are two ways to set the options. +The first is with the +.Cs o +command of rogue; +the second is with the +.Cs ROGUEOPTS +environment variable\**. +.(f +\** On Version 6 systems, +there is no equivalent of the ROGUEOPTS feature. +.br +.)f +.br +.sh 3 "Using the `o' command" +.pp +When you type +.Cs o +in rogue, +it clears the screen +and displays the current settings for all the options. +It then places the cursor by the value of the first option +and waits for you to type. +You can type a \*R +which means to go to the next option, +a +.Cs \- +which means to go to the previous option, +an \*E +which means to return to the game, +or you can give the option a value. +For boolean options this merely involves typing +.Cs t +for true or +.Cs f +for false. +For string options, +type the new value followed by a \*R. +.sh 3 "Using the ROGUEOPTS variable" +.pp +The ROGUEOPTS variable is a string +containing a comma separated list of initial values +for the various options. +Boolean variables can be turned on by listing their name +or turned off by putting a +.Cs no +in front of the name. +Thus to set up an environment variable so that +.b jump +is on, +.b terse +is off, +and the +.b name +is set to \*(lqBlue Meanie\*(rq, +use the command +.nf +.ti +3n +% setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"\** +.fi +.(f +\** +For those of you who use the Bourne shell sh (1), the commands would be +.in +3 +.nf +$ ROGUEOPTS="jump,noterse,name=Blue Meanie" +$ export ROGUEOPTS +.fi +.in +0 +.)f +.sh 2 "Option list" +.pp +Here is a list of the options +and an explanation of what each one is for. +The default value for each is enclosed in square brackets. +For character string options, +input over fifty characters will be ignored. +.ip "\fBterse\fP [\fI\^noterse\^\fP]" +Useful for those who are tired of the sometimes lengthy messages of rogue. +This is a useful option for playing on slow terminals, +so this option defaults to +.i terse +if you +are on a slow (1200 baud or under) terminal. +.ip "\fBjump\fP [\fI\^nojump\^\fP]" +If this option is set, +running moves will not be displayed +until you reach the end of the move. +This saves considerable cpu and display time. +This option defaults to +.i jump +if you are using a slow terminal. +.ip "\fBflush\fP [\fI\^noflush\^\fP]" +All typeahead is thrown away after each round of battle. +This is useful for those who type far ahead +and then watch in dismay as a Bat kills them. +.ip "\fBseefloor\fP [\fI\^seefloor\^\fP]" +Display the floor around you on the screen +as you move through dark rooms. +Due to the amount of characters generated, +this option defaults to +.i noseefloor +if you are using a slow terminal. +.ip "\fBpassgo\fP [\fI\^nopassgo\^\fP]" +Follow turnings in passageways. +If you run in a passage +and you run into stone or a wall, +rogue will see if it can turn to the right or left. +If it can only turn one way, +it will turn that way. +If it can turn either or neither, +it will stop. +This algorithm can sometimes lead to slightly confusing occurrences +which is why it defaults to \fInopassgo\fP. +.ip "\fBtombstone\fP [\fI\^tombstone\^\fP]" +Print out the tombstone at the end if you get killed. +This is nice but slow, so you can turn it off if you like. +.ip "\fBinven\fP [\fI\^overwrite\^\fP]" +Inventory type. +This can have one of three values: +.i overwrite , +.i slow , +or +.i clear . +With +.i overwrite +the top lines of the map are overwritten +with the list +when inventory is requested +or when +\*(lqWhich item do you wish to \fB. . .\fP? \*(rq questions +are answered with a +.Cs * . +However, if the list is longer than a screenful, +the screen is cleared. +With +.i slow , +lists are displayed one item at a time on the top of the screen, +and with +.i clear , +the screen is cleared, +the list is displayed, +and then the dungeon level is re-displayed. +Due to speed considerations, +.i clear +is the default for terminals without +clear-to-end-of-line capabilities. +.ip "\fBname\fP [account name]" +This is the name of your character. +It is used if you get on the top ten scorer's list. +.ip "\fBfruit\fP [\fI\^slime-mold\^\fP]" +This should hold the name of a fruit that you enjoy eating. +It is basically a whimsey that rogue uses in a couple of places. +.ip "\fBfile\fP [\fI\^~/rogue.save\^\fP]" +The default file name for saving the game. +If your phone is hung up by accident, +rogue will automatically save the game in this file. +The file name may start with the special character +.Cs ~ +which expands to be your home directory. +.sh 1 Scoring +.pp +Rogue usually maintains a list +of the top scoring people or scores on your machine. +Depending on how it is set up, +it can post either the top scores +or the top players. +In the latter case, +each account on the machine +can post only one non-winning score on this list. +If you score higher than someone else on this list, +or better your previous score on the list, +you will be inserted in the proper place +under your current name. +How many scores are kept +can also be set up by whoever installs it on your machine. +.pp +If you quit the game, you get out with all of your gold intact. +If, however, you get killed in the Dungeons of Doom, +your body is forwarded to your next-of-kin, +along with 90% of your gold; +ten percent of your gold is kept by the Dungeons' wizard as a fee\**. +.(f +\** The Dungeon's wizard is named Wally the Wonder Badger. +Invocations should be accompanied by a sizable donation. +.)f +This should make you consider whether you want to take one last hit +at that monster and possibly live, +or quit and thus stop with whatever you have. +If you quit, you do get all your gold, +but if you swing and live, you might find more. +.pp +If you just want to see what the current top players/games list is, +you can type +.ti +1i +.nf +% rogue \-s +.br +.sh 1 Acknowledgements +.pp +Rogue was originally conceived of by Glenn Wichman and Michael Toy. +Ken Arnold and Michael Toy then smoothed out the user interface, +and added jillions of new features. +We would like to thank +Bob Arnold, +Michelle Busch, +Andy Hatcher, +Kipp Hickman, +Mark Horton, +Daniel Jensen, +Bill Joy, +Joe Kalash, +Steve Maurer, +Marty McNary, +Jan Miller, +and +Scott Nelson +for their ideas and assistance; +and also the teeming multitudes +who graciously ignored work, school, and social life to play rogue +and send us bugs, complaints, suggestions, and just plain flames. +And also Mom. diff --git a/src/cc/rogue/rogue.me.in b/src/cc/rogue/rogue.me.in new file mode 100644 index 000000000..a73d1504e --- /dev/null +++ b/src/cc/rogue/rogue.me.in @@ -0,0 +1,892 @@ +.\" +.\" @(#)rogue.me 6.2 (Berkeley) 4/28/86 +.\" +.\" Rogue: Exploring the Dungeons of Doom +.\" Copyright (C) 1980-1983, 1985, 1986 Michael Toy, Ken Arnold and Glenn Wichman +.\" All rights reserved. +.\" +.\" See the file LICENSE.TXT for full copyright and licensing information. +.\" +.ds E \s-2\s0 +.ds R \s-2\s0 +.ds U \s-2UNIX\s0 +.ie t .ds _ \d\(mi\u +.el .ds _ _ +.de Cs +\&\\$3\*(lq\\$1\*(rq\\$2 +.. +.sp 5 +.ce 1000 +.ps +4 +.vs +4p +.b +A Guide to the Dungeons of Doom +.r +.vs +.ps +.sp 2 +.i +Michael C. Toy +Kenneth C. R. C. Arnold +.r +.sp 2 +Computer Systems Research Group +Department of Electrical Engineering and Computer Science +University of California +Berkeley, California 94720 +.sp 4 +.i ABSTRACT +.ce 0 +.(b I F +.bi Rogue +is a visual CRT based fantasy game +which runs under the \*U\(dg timesharing system. +.(f +\fR\(dg\*U is a trademark of Bell Laboratories\fP +.)f +This paper describes how to play rogue, +and gives a few hints +for those who might otherwise get lost in the Dungeons of Doom. +.)b +\".he '''\fBA Guide to the Dungeons of Doom\fP' +\" .fo ''- % -'' +.eh 'USD:33-%''A Guide to the Dungeons of Doom' +.oh 'A Guide to the Dungeons of Doom''USD:33-%' +.sh 1 Introduction +.pp +You have just finished your years as a student at the local fighter's guild. +After much practice and sweat you have finally completed your training +and are ready to embark upon a perilous adventure. +As a test of your skills, +the local guildmasters have sent you into the Dungeons of Doom. +Your task is to return with the Amulet of Yendor. +Your reward for the completion of this task +will be a full membership in the local guild. +In addition, +you are allowed to keep all the loot you bring back from the dungeons. +.pp +In preparation for your journey, +you are given an enchanted mace, +a bow, and a quiver of arrows +taken from a dragon's hoard in the far off Dark Mountains. +You are also outfitted with elf-crafted armor +and given enough food to reach the dungeons. +You say goodbye to family and friends for what may be the last time +and head up the road. +.pp +You set out on your way to the dungeons +and after several days of uneventful travel, +you see the ancient ruins +that mark the entrance to the Dungeons of Doom. +It is late at night, +so you make camp at the entrance +and spend the night sleeping under the open skies. +In the morning you gather your weapons, +put on your armor, +eat what is almost your last food, +and enter the dungeons. +.sh 1 "What is going on here?" +.pp +You have just begun a game of rogue. +Your goal is to grab as much treasure as you can, +find the Amulet of Yendor, +and get out of the Dungeons of Doom alive. +On the screen, +a map of where you have been +and what you have seen on the current dungeon level is kept. +As you explore more of the level, +it appears on the screen in front of you. +.pp +Rogue differs from most computer fantasy games in that it is screen oriented. +Commands are all one or two keystrokes\** +.(f +\** As opposed to pseudo English sentences. +.)f +and the results of your commands +are displayed graphically on the screen rather +than being explained in words.\** +.(f +\** A minimum screen size of 24 lines by 80 columns is required. +If the screen is larger, only the 24x80 section will be used +for the map. +.)f +.pp +Another major difference between rogue and other computer fantasy games +is that once you have solved all the puzzles in a standard fantasy game, +it has lost most of its excitement and it ceases to be fun. +Rogue, +on the other hand, +generates a new dungeon every time you play it +and even the author finds it an entertaining and exciting game. +.sh 1 "What do all those things on the screen mean?" +.pp +In order to understand what is going on in rogue +you have to first get some grasp of what rogue is doing with the screen. +The rogue screen is intended +to replace the \*(lqYou can see ...\*(rq descriptions +of standard fantasy games. +Figure 1 is a sample of what a rogue screen might look like. +.(z +.hl +.nf +.TS +center; +ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce0 ce. +- - - - - - - - - - - - +| . . . . . . . . . . + +| . . @ . . . . ] . . | +| . . . . B . . . . . | +| . . . . . . . . . . | +- - - - - + - - - - - - +.TE + + +.ce 1000 +Level: 1 Gold: 0 Hp: 12(12) Str: 16(16) Arm: 4 Exp: 1/0 + +Figure 1 +.ce +.hl +.)z +.sh 2 "The bottom line" +.pp +At the bottom line of the screen +are a few pieces of cryptic information +describing your current status. +Here is an explanation of what these things mean: +.ip Level \w'Level\ \ 'u +This number indicates how deep you have gone in the dungeon. +It starts at one and goes up as you go deeper into the dungeon. +.ip Gold \w'Level\ \ 'u +The number of gold pieces you have managed to find +and keep with you so far. +.ip Hp \w'Level\ \ 'u +Your current and maximum health points. +Health points indicate how much damage you can take before you die. +The more you get hit in a fight, +the lower they get. +You can regain health points by resting. +The number in parentheses +is the maximum number your health points can reach. +.ip Str \w'Level\ \ 'u +Your current strength and maximum ever strength. +This can be any integer less than or equal to 31, +or greater than or equal to three. +The higher the number, +the stronger you are. +The number in the parentheses +is the maximum strength you have attained so far this game. +.ip Arm \w'Level\ \ 'u +Your current armor protection. +This number indicates how effective your armor is +in stopping blows from unfriendly creatures. +The higher this number is, +the more effective the armor. +.ip Exp \w'Level\ \ 'u +These two numbers give your current experience level +and experience points. +As you do things, +you gain experience points. +At certain experience point totals, +you gain an experience level. +The more experienced you are, +the better you are able to fight and to withstand magical attacks. +.sh 2 "The top line" +.pp +The top line of the screen is reserved +for printing messages that describe things +that are impossible to represent visually. +If you see a \*(lq--More--\*(rq on the top line, +this means that rogue wants to print another message on the screen, +but it wants to make certain +that you have read the one that is there first. +To read the next message, +just type a space. +.sh 2 "The rest of the screen" +.pp +The rest of the screen is the map of the level +as you have explored it so far. +Each symbol on the screen represents something. +Here is a list of what the various symbols mean: +.ip @ +This symbol represents you, the adventurer. +.ip "-\^|" +These symbols represent the walls of rooms. +.ip + +A door to/from a room. +.ip . +The floor of a room. +.ip # +The floor of a passage between rooms. +.ip * +A pile or pot of gold. +.ip ) +A weapon of some sort. +.ip ] +A piece of armor. +.ip ! +A flask containing a magic potion. +.ip ? +A piece of paper, usually a magic scroll. +.ip = +A ring with magic properties +.ip / +A magical staff or wand +.ip ^ +A trap, watch out for these. +.ip % +A staircase to other levels +.ip : +A piece of food. +.ip A-Z +The uppercase letters +represent the various inhabitants of the Dungeons of Doom. +Watch out, they can be nasty and vicious. +.sh 1 Commands +.pp +Commands are given to rogue by typing one or two characters. +Most commands can be preceded by a count to repeat them +(e.g. typing +.Cs 10s +will do ten searches). +Commands for which counts make no sense +have the count ignored. +To cancel a count or a prefix, +type \*E. +The list of commands is rather long, +but it can be read at any time during the game with the +.Cs ? +command. +Here it is for reference, +with a short explanation of each command. +.ip ? +The help command. +Asks for a character to give help on. +If you type a +.Cs * , +it will list all the commands, +otherwise it will explain what the character you typed does. +.ip / +This is the \*(lqWhat is that on the screen?\*(rq command. +A +.Cs / +followed by any character that you see on the level, +will tell you what that character is. +For instance, +typing +.Cs /@ +will tell you that the +.Cs @ +symbol represents you, the player. +.ip "h, H, ^H" +Move left. +You move one space to the left. +If you use upper case +.Cs h , +you will continue to move left until you run into something. +This works for all movement commands +(e.g. +.Cs L +means run in direction +.Cs l ) +If you use the \*(lqcontrol\*(rq +.Cs h , +you will continue moving in the specified direction +until you pass something interesting or run into a wall. +You should experiment with this, +since it is a very useful command, +but very difficult to describe. +This also works for all movement commands. +.ip j +Move down. +.ip k +Move up. +.ip l +Move right. +.ip y +Move diagonally up and left. +.ip u +Move diagonally up and right. +.ip b +Move diagonally down and left. +.ip n +Move diagonally down and right. +.ip t +Throw an object. +This is a prefix command. +When followed with a direction +it throws an object in the specified direction. +(e.g. type +.Cs th +to throw +something to the left.) +.ip f +Fight until someone dies. +When followed with a direction +this will force you to fight the creature in that direction +until either you or it bites the big one. +.ip m +Move onto something without picking it up. +This will move you one space in the direction you specify and, +if there is an object there you can pick up, +it won't do it. +.ip z +Zap prefix. +Point a staff or wand in a given direction +and fire it. +Even non-directional staves must be pointed in some direction +to be used. +.ip ^ +Identify trap command. +If a trap is on your map +and you can't remember what type it is, +you can get rogue to remind you +by getting next to it and typing +.Cs ^ +followed by the direction that would move you on top of it. +.ip s +Search for traps and secret doors. +Examine each space immediately adjacent to you +for the existence of a trap or secret door. +There is a large chance that even if there is something there, +you won't find it, +so you might have to search a while before you find something. +.ip > +Climb down a staircase to the next level. +Not surprisingly, this can only be done if you are standing on staircase. +.ip < +Climb up a staircase to the level above. +This can't be done without the Amulet of Yendor in your possession. +.ip "." +Rest. +This is the \*(lqdo nothing\*(rq command. +This is good for waiting and healing. +.ip , +Pick up something. +This picks up whatever you are currently standing on, +if you are standing on anything at all. +.ip i +Inventory. +List what you are carrying in your pack. +.ip I +Selective inventory. +Tells you what a single item in your pack is. +.ip q +Quaff one of the potions you are carrying. +.ip r +Read one of the scrolls in your pack. +.ip e +Eat food from your pack. +.ip w +Wield a weapon. +Take a weapon out of your pack and carry it for use in combat, +replacing the one you are currently using (if any). +.ip W +Wear armor. +You can only wear one suit of armor at a time. +This takes extra time. +.ip T +Take armor off. +You can't remove armor that is cursed. +This takes extra time. +.ip P +Put on a ring. +You can wear only two rings at a time +(one on each hand). +If you aren't wearing any rings, +this command will ask you which hand you want to wear it on, +otherwise, it will place it on the unused hand. +The program assumes that you wield your sword in your right hand. +.ip R +Remove a ring. +If you are only wearing one ring, +this command takes it off. +If you are wearing two, +it will ask you which one you wish to remove, +.ip d +Drop an object. +Take something out of your pack and leave it lying on the floor. +Only one object can occupy each space. +You cannot drop a cursed object at all +if you are wielding or wearing it. +.ip c +Call an object something. +If you have a type of object in your pack +which you wish to remember something about, +you can use the call command to give a name to that type of object. +This is usually used when you figure out what a +potion, scroll, ring, or staff is +after you pick it up, +or when you want to remember +which of those swords in your pack you were wielding. +.ip D +Print out which things you've discovered something about. +This command will ask you what type of thing you are interested in. +If you type the character for a given type of object +(\fIe.g.\fP +.Cs ! +for potion) +it will tell you which kinds of that type of object you've discovered +(\fIi.e.\fP, figured out what they are). +This command works for potions, scrolls, rings, and staves and wands. +.ip o +Examine and set options. +This command is further explained in the section on options. +.ip ^R +Redraws the screen. +Useful if spurious messages or transmission errors +have messed up the display. +.ip ^P +Print last message. +Useful when a message disappears before you can read it. +This only repeats the last message +that was not a mistyped command +so that you don't loose anything by accidentally typing +the wrong character instead of ^P. +.ip \*E +Cancel a command, prefix, or count. +.ip ! +Escape to a shell for some commands. +.ip Q +Quit. +Leave the game. +.ip S +Save the current game in a file. +It will ask you whether you wish to use the default save file. +.i Caveat : +Rogue won't let you start up a copy of a saved game, +and it removes the save file as soon as you start up a restored game. +This is to prevent people from saving a game just before a dangerous position +and then restarting it if they die. +To restore a saved game, +give the file name as an argument to rogue. +As in +.ti +1i +.nf +% rogue \fIsave\*_file\fP +.ip +To restart from the default save file (see below), +run +.ti +1i +.nf +% rogue \-r +.ip v +Prints the program version number. +.ip ) +Print the weapon you are currently wielding +.ip ] +Print the armor you are currently wearing +.ip = +Print the rings you are currently wearing +.ip @ +Reprint the status line on the message line +.sh 1 Rooms +.pp +Rooms in the dungeons are either lit or dark. +If you walk into a lit room, +the entire room will be drawn on the screen as soon as you enter. +If you walk into a dark room, +it will only be displayed as you explore it. +Upon leaving a room, +all monsters inside the room +are erased from the screen. +In the darkness you can only see one space +in all directions around you. +A corridor is always dark. +.sh 1 Fighting +.pp +If you see a monster and you wish to fight it, +just attempt to run into it. +Many times a monster you find will mind its own business +unless you attack it. +It is often the case that discretion is the better part of valor. +.sh 1 "Objects you can find" +.pp +When you find something in the dungeon, +it is common to want to pick the object up. +This is accomplished in rogue by walking over the object +(unless you use the +.Cs m +prefix, see above). +If you are carrying too many things, +the program will tell you and it won't pick up the object, +otherwise it will add it to your pack +and tell you what you just picked up. +.pp +Many of the commands that operate on objects must prompt you +to find out which object you want to use. +If you change your mind and don't want to do that command after all, +just type an \*E and the command will be aborted. +.pp +Some objects, like armor and weapons, +are easily differentiated. +Others, like scrolls and potions, +are given labels which vary according to type. +During a game, +any two of the same kind of object +with the same label +are the same type. +However, +the labels will vary from game to game. +.pp +When you use one of these labeled objects, +if its effect is obvious, +rogue will remember what it is for you. +If it's effect isn't extremely obvious +you will be asked what you want to scribble on it +so you will recognize it later, +or you can use the +.Cs call +command +(see above). +.sh 2 Weapons +.pp +Some weapons, +like arrows, +come in bunches, +but most come one at a time. +In order to use a weapon, +you must wield it. +To fire an arrow out of a bow, +you must first wield the bow, +then throw the arrow. +You can only wield one weapon at a time, +but you can't change weapons if the one +you are currently wielding is cursed. +The commands to use weapons are +.Cs w +(wield) +and +.Cs t +(throw). +.sh 2 Armor +.pp +There are various sorts of armor lying around in the dungeon. +Some of it is enchanted, +some is cursed, +and some is just normal. +Different armor types have different armor protection. +The higher the armor protection, +the more protection the armor affords against the blows of monsters. +Here is a list of the various armor types and their normal armor protection: +.(b +.TS +box center; +l r. +\ \ \fIType Protection\fP +None 0 +Leather armor 2 +Studded leather / Ring mail 3 +Scale mail 4 +Chain mail 5 +Banded mail / Splint mail 6 +Plate mail 7 +.TE +.)b +.lp +If a piece of armor is enchanted, +its armor protection will be higher than normal. +If a suit of armor is cursed, +its armor protection will be lower, +and you will not be able to remove it. +However, not all armor with a protection that is lower than normal is cursed. +.pp +The commands to use weapons are +.Cs W +(wear) +and +.Cs T +(take off). +.sh 2 Scrolls +.pp +Scrolls come with titles in an unknown tongue\**. +.(f +\** Actually, it's a dialect spoken only by the twenty-seven members +of a tribe in Outer Mongolia, +but you're not supposed to +.i know +that. +.)f +After you read a scroll, +it disappears from your pack. +The command to use a scroll is +.Cs r +(read). +.sh 2 Potions +.pp +Potions are labeled by the color of the liquid inside the flask. +They disappear after being quaffed. +The command to use a scroll is +.Cs q +(quaff). +.sh 2 "Staves and Wands" +.pp +Staves and wands do the same kinds of things. +Staves are identified by a type of wood; +wands by a type of metal or bone. +They are generally things you want to do to something +over a long distance, +so you must point them at what you wish to affect +to use them. +Some staves are not affected by the direction they are pointed, though. +Staves come with multiple magic charges, +the number being random, +and when they are used up, +the staff is just a piece of wood or metal. +.pp +The command to use a wand or staff is +.Cs z +(zap) +.sh 2 Rings +.pp +Rings are very useful items, +since they are relatively permanent magic, +unlike the usually fleeting effects of potions, scrolls, and staves. +Of course, +the bad rings are also more powerful. +Most rings also cause you to use up food more rapidly, +the rate varying with the type of ring. +Rings are differentiated by their stone settings. +The commands to use rings are +.Cs P +(put on) +and +.Cs R +(remove). +.sh 2 Food +.pp +Food is necessary to keep you going. +If you go too long without eating you will faint, +and eventually die of starvation. +The command to use food is +.Cs e +(eat). +.sh 1 Options +.pp +Due to variations in personal tastes +and conceptions of the way rogue should do things, +there are a set of options you can set +that cause rogue to behave in various different ways. +.sh 2 "Setting the options" +.pp +There are two ways to set the options. +The first is with the +.Cs o +command of rogue; +the second is with the +.Cs ROGUEOPTS +environment variable\**. +.(f +\** On Version 6 systems, +there is no equivalent of the ROGUEOPTS feature. +.br +.)f +.br +.sh 3 "Using the `o' command" +.pp +When you type +.Cs o +in rogue, +it clears the screen +and displays the current settings for all the options. +It then places the cursor by the value of the first option +and waits for you to type. +You can type a \*R +which means to go to the next option, +a +.Cs \- +which means to go to the previous option, +an \*E +which means to return to the game, +or you can give the option a value. +For boolean options this merely involves typing +.Cs t +for true or +.Cs f +for false. +For string options, +type the new value followed by a \*R. +.sh 3 "Using the ROGUEOPTS variable" +.pp +The ROGUEOPTS variable is a string +containing a comma separated list of initial values +for the various options. +Boolean variables can be turned on by listing their name +or turned off by putting a +.Cs no +in front of the name. +Thus to set up an environment variable so that +.b jump +is on, +.b terse +is off, +and the +.b name +is set to \*(lqBlue Meanie\*(rq, +use the command +.nf +.ti +3n +% setenv ROGUEOPTS "jump,noterse,name=Blue Meanie"\** +.fi +.(f +\** +For those of you who use the Bourne shell sh (1), the commands would be +.in +3 +.nf +$ ROGUEOPTS="jump,noterse,name=Blue Meanie" +$ export ROGUEOPTS +.fi +.in +0 +.)f +.sh 2 "Option list" +.pp +Here is a list of the options +and an explanation of what each one is for. +The default value for each is enclosed in square brackets. +For character string options, +input over fifty characters will be ignored. +.ip "\fBterse\fP [\fI\^noterse\^\fP]" +Useful for those who are tired of the sometimes lengthy messages of rogue. +This is a useful option for playing on slow terminals, +so this option defaults to +.i terse +if you +are on a slow (1200 baud or under) terminal. +.ip "\fBjump\fP [\fI\^nojump\^\fP]" +If this option is set, +running moves will not be displayed +until you reach the end of the move. +This saves considerable cpu and display time. +This option defaults to +.i jump +if you are using a slow terminal. +.ip "\fBflush\fP [\fI\^noflush\^\fP]" +All typeahead is thrown away after each round of battle. +This is useful for those who type far ahead +and then watch in dismay as a Bat kills them. +.ip "\fBseefloor\fP [\fI\^seefloor\^\fP]" +Display the floor around you on the screen +as you move through dark rooms. +Due to the amount of characters generated, +this option defaults to +.i noseefloor +if you are using a slow terminal. +.ip "\fBpassgo\fP [\fI\^nopassgo\^\fP]" +Follow turnings in passageways. +If you run in a passage +and you run into stone or a wall, +rogue will see if it can turn to the right or left. +If it can only turn one way, +it will turn that way. +If it can turn either or neither, +it will stop. +This algorithm can sometimes lead to slightly confusing occurrences +which is why it defaults to \fInopassgo\fP. +.ip "\fBtombstone\fP [\fI\^tombstone\^\fP]" +Print out the tombstone at the end if you get killed. +This is nice but slow, so you can turn it off if you like. +.ip "\fBinven\fP [\fI\^overwrite\^\fP]" +Inventory type. +This can have one of three values: +.i overwrite , +.i slow , +or +.i clear . +With +.i overwrite +the top lines of the map are overwritten +with the list +when inventory is requested +or when +\*(lqWhich item do you wish to \fB. . .\fP? \*(rq questions +are answered with a +.Cs * . +However, if the list is longer than a screenful, +the screen is cleared. +With +.i slow , +lists are displayed one item at a time on the top of the screen, +and with +.i clear , +the screen is cleared, +the list is displayed, +and then the dungeon level is re-displayed. +Due to speed considerations, +.i clear +is the default for terminals without +clear-to-end-of-line capabilities. +.ip "\fBname\fP [account name]" +This is the name of your character. +It is used if you get on the top ten scorer's list. +.ip "\fBfruit\fP [\fI\^slime-mold\^\fP]" +This should hold the name of a fruit that you enjoy eating. +It is basically a whimsey that rogue uses in a couple of places. +.ip "\fBfile\fP [\fI\^~/rogue.save\^\fP]" +The default file name for saving the game. +If your phone is hung up by accident, +rogue will automatically save the game in this file. +The file name may start with the special character +.Cs ~ +which expands to be your home directory. +.sh 1 Scoring +.pp +Rogue usually maintains a list +of the top scoring people or scores on your machine. +Depending on how it is set up, +it can post either the top scores +or the top players. +In the latter case, +each account on the machine +can post only one non-winning score on this list. +If you score higher than someone else on this list, +or better your previous score on the list, +you will be inserted in the proper place +under your current name. +How many scores are kept +can also be set up by whoever installs it on your machine. +.pp +If you quit the game, you get out with all of your gold intact. +If, however, you get killed in the Dungeons of Doom, +your body is forwarded to your next-of-kin, +along with 90% of your gold; +ten percent of your gold is kept by the Dungeons' wizard as a fee\**. +.(f +\** The Dungeon's wizard is named Wally the Wonder Badger. +Invocations should be accompanied by a sizable donation. +.)f +This should make you consider whether you want to take one last hit +at that monster and possibly live, +or quit and thus stop with whatever you have. +If you quit, you do get all your gold, +but if you swing and live, you might find more. +.pp +If you just want to see what the current top players/games list is, +you can type +.ti +1i +.nf +% @PROGRAM@ \-s +.br +.sh 1 Acknowledgements +.pp +Rogue was originally conceived of by Glenn Wichman and Michael Toy. +Ken Arnold and Michael Toy then smoothed out the user interface, +and added jillions of new features. +We would like to thank +Bob Arnold, +Michelle Busch, +Andy Hatcher, +Kipp Hickman, +Mark Horton, +Daniel Jensen, +Bill Joy, +Joe Kalash, +Steve Maurer, +Marty McNary, +Jan Miller, +and +Scott Nelson +for their ideas and assistance; +and also the teeming multitudes +who graciously ignored work, school, and social life to play rogue +and send us bugs, complaints, suggestions, and just plain flames. +And also Mom. diff --git a/src/cc/rogue/rogue.png b/src/cc/rogue/rogue.png new file mode 100644 index 000000000..fc3779e83 Binary files /dev/null and b/src/cc/rogue/rogue.png differ diff --git a/src/cc/rogue/rogue.spec b/src/cc/rogue/rogue.spec new file mode 100644 index 000000000..6c8770fdc --- /dev/null +++ b/src/cc/rogue/rogue.spec @@ -0,0 +1,107 @@ +Name: rogue +Version: 5.4.4 +Release: 1%{?dist} +Summary: The original graphical adventure game + +Group: Amusements/Games +License: BSD +URL: http://rogue.rogueforge.net/ +Source0: http://rogue.rogueforge.net/files/rogue5.4/rogue5.4.4-src.tar.gz +Source1: rogue.desktop +Source2: rogue.png +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +BuildRequires: desktop-file-utils +BuildRequires: ncurses-devel + +%description +The one, the only, the original graphical adventure game that spawned +an entire genre. + +%prep +%setup -q -n %{name}%{version} + + +%build +%configure --enable-setgid=games --enable-scorefile=%{_var}/games/roguelike/rogue54.scr --enable-lockfile=%{_var}/games/roguelike/rogue54.lck +make %{_smp_mflags} + + +%install +rm -rf $RPM_BUILD_ROOT + +make install DESTDIR=$RPM_BUILD_ROOT + +desktop-file-install --vendor fedora \ + --dir ${RPM_BUILD_ROOT}%{_datadir}/applications \ + %{SOURCE1} +mkdir -p $RPM_BUILD_ROOT/%{_datadir}/icons/hicolor/32x32/apps/ +install -p -m 644 %{SOURCE2} $RPM_BUILD_ROOT/%{_datadir}/icons/hicolor/32x32/apps/ + + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +touch --no-create %{_datadir}/icons/hicolor || : +if [ -x %{_bindir}/gtk-update-icon-cache ]; then + %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || : +fi + +%postun +touch --no-create %{_datadir}/icons/hicolor || : +if [ -x %{_bindir}/gtk-update-icon-cache ]; then + %{_bindir}/gtk-update-icon-cache --quiet %{_datadir}/icons/hicolor || : +fi + + +%files +%defattr(-,root,root,-) +%attr(2755,games,games) %{_bindir}/rogue +%{_mandir}/man6/rogue.6.gz +%{_datadir}/applications/fedora-%{name}.desktop +%{_datadir}/icons/hicolor/32x32/apps/rogue.png +%dir %attr(0775,games,games) %{_var}/games/roguelike +%config(noreplace) %attr(0664,games,games) %{_var}/games/roguelike/rogue54.scr +%doc %{_docdir}/%{name}-%{version} + + +%changelog +* Sun Sep 2 2007 Wart 5.4.4-1 +- Update to 5.4.4 + +* Mon Aug 20 2007 Wart 5.4.3-1 +- Update to 5.4.3 + +* Sun Jul 15 2007 Wart 5.4.2-9 +- New upstream home page and download URL +- Add patch when reading long values from the save file on 64-bit arch + (BZ #248283) +- Add patch removing many compiler warnings +- Use proper version in the .desktop file + +* Sat Mar 3 2007 Wart 5.4.2-8 +- Use better sourceforge download url +- Use more precise desktop file categories + +* Mon Aug 28 2006 Wart 5.4.2-7 +- Rebuild for Fedora Extras + +* Tue May 16 2006 Wart 5.4.2-6 +- Added empty initial scoreboard file. + +* Mon May 15 2006 Wart 5.4.2-5 +- Better setuid/setgid handling (again) (BZ #187392) + +* Thu Mar 30 2006 Wart 5.4.2-4 +- Better setuid/setgid handling (BZ #187392) +- Resize desktop icon to match directory name + +* Mon Mar 13 2006 Wart 5.4.2-3 +- Added icon for .desktop file. + +* Sun Mar 12 2006 Wart 5.4.2-2 +- Added missing BR: ncurses-devel, desktop-file-utils + +* Sat Feb 25 2006 Wart 5.4.2-1 +- Initial spec file. diff --git a/src/cc/rogue/rogue54.sln b/src/cc/rogue/rogue54.sln new file mode 100644 index 000000000..751959465 --- /dev/null +++ b/src/cc/rogue/rogue54.sln @@ -0,0 +1,27 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.25420.1 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "rogue54", "rogue54.vcxproj", "{9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|Win32.ActiveCfg = Debug|Win32 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|Win32.Build.0 = Debug|Win32 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|x64.ActiveCfg = Debug|x64 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Debug|x64.Build.0 = Debug|x64 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|Win32.ActiveCfg = Release|Win32 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|Win32.Build.0 = Release|Win32 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|x64.ActiveCfg = Release|x64 + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/src/cc/rogue/rogue54.vcproj b/src/cc/rogue/rogue54.vcproj new file mode 100644 index 000000000..7e39b98dd --- /dev/null +++ b/src/cc/rogue/rogue54.vcproj @@ -0,0 +1,396 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/cc/rogue/rogue54.vcxproj b/src/cc/rogue/rogue54.vcxproj new file mode 100644 index 000000000..344598dd8 --- /dev/null +++ b/src/cc/rogue/rogue54.vcxproj @@ -0,0 +1,257 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {9EA0D326-8097-4ADA-82EA-4DB1F5CAA8F6} + Win32Proj + 8.1 + + + + Application + v140 + MultiByte + + + Application + v140 + MultiByte + false + + + Application + v140 + MultiByte + + + Application + v140 + MultiByte + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>14.0.25431.1 + + + Debug\ + Debug\ + true + + + true + + + Release\ + Release\ + false + + + false + + + + Disabled + Default + ../pdcurses;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;_CRT_SECURE_NO_DEPRECATE;ALLSCORES;MASTER;SCOREFILE="rogue54.scr";LOCKFILE="rogue54.lck";%(PreprocessorDefinitions) + true + false + + EnableFastChecks + MultiThreaded + true + true + false + true + + + Level4 + EditAndContinue + CompileAsC + + + Ws2_32.lib;pdcurses.lib;advapi32.lib;shfolder.lib;user32.lib;%(AdditionalDependencies) + $(OutDir)rogue54.exe + ..\pdcurses;%(AdditionalLibraryDirectories) + false + true + $(OutDir)rogue54.pdb + Console + MachineX86 + + + + + Disabled + Default + $(ProjectDir)x86_64-w64-msvc\deps\install\include;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_WINDOWS;PDC_DLL_BUILD;PDC_WIDE;PDCDEBUG;_CRT_SECURE_NO_DEPRECATE;ALLSCORES;SCOREFILE="rogue54.scr";LOCKFILE="rogue54.lck";%(PreprocessorDefinitions) + true + false + + + EnableFastChecks + MultiThreaded + true + true + false + true + + + + + Level4 + ProgramDatabase + CompileAsC + + + Ws2_32.lib;wincon\pdcurses.lib;libcurl_imp.lib;advapi32.lib;shfolder.lib;user32.lib;%(AdditionalDependencies) + $(OutDir)rogue54.exe + $(ProjectDir)x86_64-w64-msvc\deps\install\lib;$(ProjectDir)x86_64-w64-msvc\deps\install\Release\lib;%(AdditionalLibraryDirectories) + false + true + $(OutDir)rogue54.pdb + Console + + + + + MaxSpeed + OnlyExplicitInline + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + true + MultiThreaded + true + NotUsing + Level3 + ProgramDatabase + $(ProjectDir)\x86_64-w64-msvc\include\ncursesw;$(ProjectDir)\x86_64-w64-msvc\include;%(AdditionalIncludeDirectories) + + + $(OutDir)rogue54.exe + true + Windows + true + true + MachineX86 + %(AdditionalLibraryDirectories) + %(AdditionalDependencies) + + + + + MaxSpeed + OnlyExplicitInline + true + WIN32;_WINDOWS;NDEBUG;PDC_DLL_BUILD;PDC_WIDE;PDCDEBUG;_CRT_SECURE_NO_DEPRECATE;ALLSCORES;SCOREFILE="rogue54.scr";LOCKFILE="rogue54.lck";%(PreprocessorDefinitions) + true + MultiThreaded + true + + + Level3 + ProgramDatabase + $(ProjectDir)x86_64-w64-msvc\deps\install\include;%(AdditionalIncludeDirectories) + stdafx.h + CompileAsC + + + + + $(OutDir)rogue54.exe + true + Console + true + true + $(ProjectDir)x86_64-w64-msvc\deps\install\lib;$(ProjectDir)x86_64-w64-msvc\deps\install\Release\lib;%(AdditionalLibraryDirectories) + Ws2_32.lib;wincon\pdcurses.lib;libcurl_imp.lib;advapi32.lib;shfolder.lib;user32.lib;%(AdditionalDependencies) + %(IgnoreSpecificDefaultLibraries) + false + %(ForceSymbolReferences) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/cc/rogue/rogue_build_msvc.cmd b/src/cc/rogue/rogue_build_msvc.cmd new file mode 100644 index 000000000..77e7cd852 --- /dev/null +++ b/src/cc/rogue/rogue_build_msvc.cmd @@ -0,0 +1,64 @@ +@echo off +echo Rogue Build Script by Decker (c) 2019 + +@REM Check for Visual Studio +call set "VSPATH=" +if defined VS140COMNTOOLS ( if not defined VSPATH ( + call set "VSPATH=%%VS140COMNTOOLS%%" +) ) + +@REM check if we already have the tools in the environment +if exist "%VCINSTALLDIR%" ( + goto compile +) + +if not defined VSPATH ( + echo You need Microsoft Visual Studio 15 installed + pause + exit +) + +@REM set up the environment +if exist "%VSPATH%..\..\vc\vcvarsall.bat" ( + call "%%VSPATH%%..\..\vc\vcvarsall.bat" amd64 + goto compile +) + +echo Unable to set up the environment +pause +exit + +:compile + +mkdir x86_64-w64-msvc\deps +mkdir x86_64-w64-msvc\deps\install + +pushd x86_64-w64-msvc\deps + +:compile_pdcurses +rem git clone https://github.com/wmcbrine/PDCurses PDCurses.org +git clone https://github.com/Bill-Gray/PDCurses + +set PREFIX_DIR=%CD%\install + +pushd PDCurses +mkdir build64 & pushd build64 +rem cmake -G"Visual Studio 14 2015 Win64" -DPDC_WIDE=ON -DCMAKE_INSTALL_PREFIX=%PREFIX_DIR% -DCMAKE_BUILD_TYPE=Debug -DPDCDEBUG=ON .. +cmake -G"Visual Studio 14 2015 Win64" -DPDC_WIDE=ON -DCMAKE_INSTALL_PREFIX=%PREFIX_DIR% -DCMAKE_BUILD_TYPE=Release .. +popd +rem cmake --build build64 --config Debug --target install +cmake --build build64 --config Release --target install +popd + +:compile_curl + +git clone https://github.com/curl/curl +pushd curl + +mkdir build64 & pushd build64 +cmake -G "Visual Studio 14 2015 Win64" -DCMAKE_INSTALL_PREFIX=%PREFIX_DIR% -DCMAKE_USE_WINSSL:BOOL=ON .. +cmake --build . --config Release --target libcurl +cmake --build . --config Release --target install +popd +popd + diff --git a/src/cc/rogue/rogue_player.h b/src/cc/rogue/rogue_player.h new file mode 100644 index 000000000..e6b7a69e6 --- /dev/null +++ b/src/cc/rogue/rogue_player.h @@ -0,0 +1,35 @@ +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#ifndef ROGUE_DECLARED_PACK +#define ROGUE_DECLARED_PACK + + +#define MAXPACK 23 +struct rogue_packitem +{ + int32_t type,launch,count,which,hplus,dplus,arm,flags,group; + char damage[8],hurldmg[8]; +}; +struct rogue_player +{ + int32_t gold,hitpoints,strength,level,experience,packsize,dungeonlevel,amulet; + struct rogue_packitem roguepack[MAXPACK]; +}; +int32_t rogue_replay2(uint8_t *newdata,uint64_t seed,char *keystrokes,int32_t num,struct rogue_player *player,int32_t sleepmillis); +void rogue_packitemstr(char *packitemstr,struct rogue_packitem *item); + +#endif + diff --git a/src/cc/rogue/rooms.c b/src/cc/rogue/rooms.c new file mode 100644 index 000000000..67ca701d9 --- /dev/null +++ b/src/cc/rogue/rooms.c @@ -0,0 +1,476 @@ +/* + * Create the layout for the new level + * + * @(#)rooms.c 4.45 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" + +typedef struct spot { /* position matrix for maze positions */ + int nexits; + coord exits[4]; + int used; +} SPOT; + +#define GOLDGRP 1 + +/* + * do_rooms: + * Create rooms and corridors with a connectivity graph + */ + +void +do_rooms(struct rogue_state *rs) +{ + int i; + struct room *rp; + THING *tp; + int left_out; + static coord top; + coord bsze; /* maximum room size */ + coord mp; + + bsze.x = NUMCOLS / 3; + bsze.y = NUMLINES / 3; + /* + * Clear things for a new level + */ + for (rp = rooms; rp < &rooms[MAXROOMS]; rp++) + { + rp->r_goldval = 0; + rp->r_nexits = 0; + rp->r_flags = 0; + } + /* + * Put the gone rooms, if any, on the level + */ + left_out = rnd(4); + for (i = 0; i < left_out; i++) + rooms[rnd_room()].r_flags |= ISGONE; + /* + * dig and populate all the rooms on the level + */ + for (i = 0, rp = rooms; i < MAXROOMS; rp++, i++) + { + /* + * Find upper left corner of box that this room goes in + */ + top.x = (i % 3) * bsze.x + 1; + top.y = (i / 3) * bsze.y; + if (rp->r_flags & ISGONE) + { + /* + * Place a gone room. Make certain that there is a blank line + * for passage drawing. + */ + do + { + rp->r_pos.x = top.x + rnd(bsze.x - 2) + 1; + rp->r_pos.y = top.y + rnd(bsze.y - 2) + 1; + rp->r_max.x = -NUMCOLS; + rp->r_max.y = -NUMLINES; + } until (rp->r_pos.y > 0 && rp->r_pos.y < NUMLINES-1); + continue; + } + /* + * set room type + */ + if (rnd(10) < level - 1) + { + rp->r_flags |= ISDARK; /* dark room */ + if (rnd(15) == 0) + rp->r_flags = ISMAZE; /* maze room */ + } + /* + * Find a place and size for a random room + */ + if (rp->r_flags & ISMAZE) + { + rp->r_max.x = bsze.x - 1; + rp->r_max.y = bsze.y - 1; + if ((rp->r_pos.x = top.x) == 1) + rp->r_pos.x = 0; + if ((rp->r_pos.y = top.y) == 0) + { + rp->r_pos.y++; + rp->r_max.y--; + } + } + else + do + { + rp->r_max.x = rnd(bsze.x - 4) + 4; + rp->r_max.y = rnd(bsze.y - 4) + 4; + rp->r_pos.x = top.x + rnd(bsze.x - rp->r_max.x); + rp->r_pos.y = top.y + rnd(bsze.y - rp->r_max.y); + } until (rp->r_pos.y != 0); + draw_room(rs,rp); + /* + * Put the gold in + */ + if (rnd(2) == 0 && (!amulet || level >= max_level)) + { + THING *gold; + + gold = new_item(); + gold->o_goldval = rp->r_goldval = GOLDCALC; + find_floor(rs,rp, &rp->r_gold, FALSE, FALSE); + gold->o_pos = rp->r_gold; + chat(rp->r_gold.y, rp->r_gold.x) = GOLD; + gold->o_flags = ISMANY; + gold->o_group = GOLDGRP; + gold->o_type = GOLD; + attach(lvl_obj, gold); + } + /* + * Put the monster in + */ + if (rnd(100) < (rp->r_goldval > 0 ? 80 : 25)) + { + tp = new_item(); + find_floor(rs,rp, &mp, FALSE, TRUE); + new_monster(rs,tp, randmonster(FALSE), &mp); + give_pack(rs,tp); + } + } +} + +/* + * draw_room: + * Draw a box around a room and lay down the floor for normal + * rooms; for maze rooms, draw maze. + */ + +void +draw_room(struct rogue_state *rs,struct room *rp) +{ + int y, x; + + if (rp->r_flags & ISMAZE) + do_maze(rs,rp); + else + { + vert(rp, rp->r_pos.x); /* Draw left side */ + vert(rp, rp->r_pos.x + rp->r_max.x - 1); /* Draw right side */ + horiz(rp, rp->r_pos.y); /* Draw top */ + horiz(rp, rp->r_pos.y + rp->r_max.y - 1); /* Draw bottom */ + + /* + * Put the floor down + */ + for (y = rp->r_pos.y + 1; y < rp->r_pos.y + rp->r_max.y - 1; y++) + for (x = rp->r_pos.x + 1; x < rp->r_pos.x + rp->r_max.x - 1; x++) + chat(y, x) = FLOOR; + } +} + +/* + * vert: + * Draw a vertical line + */ + +void +vert(struct room *rp, int startx) +{ + int y; + + for (y = rp->r_pos.y + 1; y <= rp->r_max.y + rp->r_pos.y - 1; y++) + chat(y, startx) = '|'; +} + +/* + * horiz: + * Draw a horizontal line + */ + +void +horiz(struct room *rp, int starty) +{ + int x; + + for (x = rp->r_pos.x; x <= rp->r_pos.x + rp->r_max.x - 1; x++) + chat(starty, x) = '-'; +} + +/* + * do_maze: + * Dig a maze + */ + +static int Maxy, Maxx, Starty, Startx; + +static SPOT maze[NUMLINES/3+1][NUMCOLS/3+1]; + + +void +do_maze(struct rogue_state *rs,struct room *rp) +{ + SPOT *sp; + int starty, startx; + static coord pos; + + for (sp = &maze[0][0]; sp <= &maze[NUMLINES / 3][NUMCOLS / 3]; sp++) + { + sp->used = FALSE; + sp->nexits = 0; + } + + Maxy = rp->r_max.y; + Maxx = rp->r_max.x; + Starty = rp->r_pos.y; + Startx = rp->r_pos.x; + starty = (rnd(rp->r_max.y) / 2) * 2; + startx = (rnd(rp->r_max.x) / 2) * 2; + pos.y = starty + Starty; + pos.x = startx + Startx; + putpass(&pos); + dig(rs,starty, startx); +} + +/* + * dig: + * Dig out from around where we are now, if possible + */ + +void +dig(struct rogue_state *rs,int y, int x) +{ + coord *cp; + int cnt, newy, newx, nexty = 0, nextx = 0; + static coord pos; + static coord del[4] = { + {2, 0}, {-2, 0}, {0, 2}, {0, -2} + }; + + for (;;) + { + if ( rs->replaydone != 0 ) + return; + cnt = 0; + for (cp = del; cp <= &del[3]; cp++) + { + newy = y + cp->y; + newx = x + cp->x; + if (newy < 0 || newy > Maxy || newx < 0 || newx > Maxx) + continue; + if (flat(newy + Starty, newx + Startx) & F_PASS) + continue; + if (rnd(++cnt) == 0) + { + nexty = newy; + nextx = newx; + } + } + if (cnt == 0) + return; + accnt_maze(y, x, nexty, nextx); + accnt_maze(nexty, nextx, y, x); + if (nexty == y) + { + pos.y = y + Starty; + if (nextx - x < 0) + pos.x = nextx + Startx + 1; + else + pos.x = nextx + Startx - 1; + } + else + { + pos.x = x + Startx; + if (nexty - y < 0) + pos.y = nexty + Starty + 1; + else + pos.y = nexty + Starty - 1; + } + putpass(&pos); + pos.y = nexty + Starty; + pos.x = nextx + Startx; + putpass(&pos); + dig(rs,nexty, nextx); + } +} + +/* + * accnt_maze: + * Account for maze exits + */ + +void +accnt_maze(int y, int x, int ny, int nx) +{ + SPOT *sp; + coord *cp; + + sp = &maze[y][x]; + for (cp = sp->exits; cp < &sp->exits[sp->nexits]; cp++) + if (cp->y == ny && cp->x == nx) + return; + cp->y = ny; + cp->x = nx; +} + +/* + * rnd_pos: + * Pick a random spot in a room + */ + +void +rnd_pos(struct room *rp, coord *cp) +{ + cp->x = rp->r_pos.x + rnd(rp->r_max.x - 2) + 1; + cp->y = rp->r_pos.y + rnd(rp->r_max.y - 2) + 1; +} + +/* + * find_floor: + * Find a valid floor spot in this room. If rp is NULL, then + * pick a new room each time around the loop. + */ +bool +find_floor(struct rogue_state *rs,struct room *rp, coord *cp, int limit, bool monst) +{ + PLACE *pp; + int cnt; + char compchar = 0; + bool pickroom; + + pickroom = (bool)(rp == NULL); + + if (!pickroom) + compchar = ((rp->r_flags & ISMAZE) ? PASSAGE : FLOOR); + cnt = limit; + for (;;) + { + if ( rs->replaydone != 0 ) + return(FALSE); + if (limit && cnt-- == 0) + return FALSE; + if (pickroom) + { + rp = &rooms[rnd_room()]; + compchar = ((rp->r_flags & ISMAZE) ? PASSAGE : FLOOR); + } + rnd_pos(rp, cp); + pp = INDEX(cp->y, cp->x); + if (monst) + { + if (pp->p_monst == NULL && step_ok(pp->p_ch)) + return TRUE; + } + else if (pp->p_ch == compchar) + return TRUE; + } +} + +/* + * enter_room: + * Code that is executed whenver you appear in a room + */ + +void +enter_room(struct rogue_state *rs,coord *cp) +{ + struct room *rp; + THING *tp; + int y, x; + char ch; + + rp = proom = roomin(rs,cp); + door_open(rs,rp); + if (!(rp->r_flags & ISDARK) && !on(player, ISBLIND)) + for (y = rp->r_pos.y; y < rp->r_max.y + rp->r_pos.y; y++) + { + move(y, rp->r_pos.x); + for (x = rp->r_pos.x; x < rp->r_max.x + rp->r_pos.x; x++) + { + tp = moat(y, x); + ch = chat(y, x); + if (tp == NULL) + if (CCHAR(inch()) != ch) + addch(ch); + else + move(y, x + 1); + else + { + tp->t_oldch = ch; + if (!see_monst(tp)) + if (on(player, SEEMONST)) + { + standout(); + addch(tp->t_disguise); + standend(); + } + else + addch(ch); + else + addch(tp->t_disguise); + } + } + } +} + +/* + * leave_room: + * Code for when we exit a room + */ + +void +leave_room(struct rogue_state *rs,coord *cp) +{ + PLACE *pp; + struct room *rp; + int y, x; + char floor; + char ch; + + rp = proom; + + if (rp->r_flags & ISMAZE) + return; + + if (rp->r_flags & ISGONE) + floor = PASSAGE; + else if (!(rp->r_flags & ISDARK) || on(player, ISBLIND)) + floor = FLOOR; + else + floor = ' '; + + proom = &passages[flat(cp->y, cp->x) & F_PNUM]; + for (y = rp->r_pos.y; y < rp->r_max.y + rp->r_pos.y; y++) + for (x = rp->r_pos.x; x < rp->r_max.x + rp->r_pos.x; x++) + { + move(y, x); + switch ( ch = CCHAR(inch()) ) + { + case FLOOR: + if (floor == ' ' && ch != ' ') + addch(' '); + break; + default: + /* + * to check for monster, we have to strip out + * standout bit + */ + if (isupper(toascii(ch))) + { + if (on(player, SEEMONST)) + { + standout(); + addch(ch); + standend(); + break; + } + pp = INDEX(y,x); + addch(pp->p_ch == DOOR ? DOOR : floor); + } + } + } + door_open(rs,rp); +} diff --git a/src/cc/rogue/save.c b/src/cc/rogue/save.c new file mode 100644 index 000000000..4df79e150 --- /dev/null +++ b/src/cc/rogue/save.c @@ -0,0 +1,462 @@ +/* + * save and restore routines + * + * @(#)save.c 4.33 (Berkeley) 06/01/83 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +//#include +//#include +//#include +//#include +#include "rogue.h" +#include "score.h" + +typedef struct stat STAT; + +extern char *version, *encstr; + +static STAT sbuf; + +/* + * save_game: + * Implement the "save game" command + */ + +void +save_game(struct rogue_state *rs) +{ + FILE *savef; + int c; + //auto + char buf[MAXSTR]; + + /* + * get file name + */ + mpos = 0; +over: + if (file_name[0] != '\0') + { + for (;;) + { + msg(rs,"save file (%s)? ", file_name); + c = readchar(rs); + mpos = 0; + if (c == ESCAPE) + { + msg(rs,""); + return; + } + else if (c == 'n' || c == 'N' || c == 'y' || c == 'Y') + break; + else + msg(rs,"please answer Y or N"); + } + if (c == 'y' || c == 'Y') + { + addstr("Yes\n"); + if ( rs->sleeptime != 0 ) + refresh(); + strcpy(buf, file_name); + goto gotfile; + } + } + + do + { + mpos = 0; + msg(rs,"file name: "); + buf[0] = '\0'; + if (get_str(rs,buf, stdscr) == QUIT) + { +quit_it: + msg(rs,""); + return; + } + mpos = 0; +gotfile: + /* + * test to see if the file exists + */ + if (stat(buf, &sbuf) >= 0) + { + for (;;) + { + msg(rs,"File exists. Do you wish to overwrite it?"); + mpos = 0; + if ((c = readchar(rs)) == ESCAPE) + goto quit_it; + if (c == 'y' || c == 'Y') + break; + else if (c == 'n' || c == 'N') + goto over; + else + msg(rs,"Please answer Y or N"); + } + msg(rs,"file name: %s", buf); + md_unlink(file_name); + } + strcpy(file_name, buf); + if ((savef = fopen(file_name, "w")) == NULL) + msg(rs,strerror(errno)); + } while (savef == NULL); + + save_file(rs,savef,1); + /* NOTREACHED */ +} + +/* + * auto_save: + * Automatically save a file. This is used if a HUP signal is + * recieved + */ + +void +auto_save(int sig) +{ + FILE *savef; + NOOP(sig); + + md_ignoreallsignals(); + if (file_name[0] != '\0' && ((savef = fopen(file_name, "w")) != NULL || + (md_unlink_open_file(file_name, savef) >= 0 && (savef = fopen(file_name, "w")) != NULL))) + save_file(&globalR,savef,1); + my_exit(0); +} + +/* + * save_file: + * Write the saved game on the file + */ + +char *rogue_packfname(struct rogue_state *rs,char *fname) +{ + sprintf(fname,"rogue.%llu.pack",(long long)rs->seed); + return(fname); +} + +void +save_file(struct rogue_state *rs,FILE *savef,int32_t guiflag) +{ + char buf[80],fname[512]; int32_t i,n,nonz,histo[0x100]; FILE *fp; + if ( rs->guiflag != 0 ) + { + mvcur(0, COLS - 1, LINES - 1, 0); + putchar('\n'); + endwin(); + resetltchars(); + md_chmod(file_name, 0400); + if ( guiflag != 0 ) + { + encwrite(version, strlen(version)+1, savef); + sprintf(buf,"%d x %d\n", LINES, COLS); + encwrite(buf,80,savef); + } + } + memset(&rs->P,0,sizeof(rs->P)); + rs_save_file(rs,savef); // sets rs->P + //fprintf(stderr,"gold.%d hp.%d strength.%d level.%d exp.%d %d\n",rs->P.gold,rs->P.hitpoints,rs->P.strength,rs->P.level,rs->P.experience,rs->P.dungeonlevel); + + n = sizeof(rs->P) - sizeof(rs->P.roguepack) + sizeof(rs->P.roguepack[0])*rs->P.packsize; + memset(histo,0,sizeof(histo)); + for (i=0; iP)[i]); + histo[((uint8_t *)&rs->P)[i]]++; + rs->playerdata[i] = ((uint8_t *)&rs->P)[i]; + } + rs->playersize = n; + //fprintf(stderr," packsize.%d playersize.%d\n",rs->P.packsize,n); + if ( (fp= fopen(rogue_packfname(rs,fname),"wb")) != 0 ) + { + fwrite(&rs->P,1,n,fp); + fclose(fp); + } + if ( 0 ) + { + for (i=nonz=0; i<0x100; i++) + if ( histo[i] != 0 ) + fprintf(stderr,"(%d %d) ",i,histo[i]), nonz++; + fprintf(stderr,"nonz.%d\n",nonz); + } + fflush(savef); + fclose(savef); + if ( guiflag != 0 ) + my_exit(0); +} + +int32_t rogue_restorepack(struct rogue_state *rs) +{ + FILE *fp; char fname[512]; int32_t retflag = -1; + memset(&rs->P,0,sizeof(rs->P)); + if ( (fp= fopen(rogue_packfname(rs,fname),"rb")) != 0 ) + { + if ( fread(&rs->P,1,sizeof(rs->P) - sizeof(rs->P.roguepack),fp) == sizeof(rs->P) - sizeof(rs->P.roguepack) ) + { + if ( rs->P.packsize > 0 && rs->P.packsize <= MAXPACK ) + { + if ( fread(&rs->P.roguepack,1,rs->P.packsize*sizeof(rs->P.roguepack[0]),fp) == rs->P.packsize*sizeof(rs->P.roguepack[0]) ) + { + fprintf(stderr,"roguepack[%d] restored\n",rs->P.packsize); + retflag = 0; + } + } + } + } + if ( retflag < 0 ) + memset(&rs->P,0,sizeof(rs->P)); + return(retflag); +} + +/* + * restore: + * Restore a saved game from a file with elaborate checks for file + * integrity from cheaters + */ +bool +restore(struct rogue_state *rs,char *file, char **envp) +{ + FILE *inf; + int syml,l, cols; + extern char **environ; + //auto + char buf[MAXSTR]; + //auto + STAT sbuf2; + if ( rs->guiflag == 0 ) + return(0); + + if (strcmp(file, "-r") == 0) + file = file_name; + + md_tstphold(); + + if ((inf = fopen(file,"r")) == NULL) + { + perror(file); + return FALSE; + } + stat(file, &sbuf2); + syml = is_symlink(file); + + fflush(stdout); + encread(buf, (unsigned) strlen(version) + 1, inf); + if (strcmp(buf, version) != 0) + { + printf("Sorry, saved game is out of date.\n"); + return FALSE; + } + encread(buf,80,inf); + sscanf(buf,"%d x %d\n", &l, &cols); + + initscr(); /* Start up cursor package */ + keypad(stdscr, 1); + + if (l > LINES) + { + endwin(); + printf("Sorry, original game was played on a screen with %d lines.\n",l); + printf("Current screen only has %d lines. Unable to restore game\n",LINES); + return(FALSE); + } + if (cols > COLS) + { + endwin(); + printf("Sorry, original game was played on a screen with %d columns.\n",cols); + printf("Current screen only has %d columns. Unable to restore game\n",COLS); + return(FALSE); + } + + hw = newwin(LINES, COLS, 0, 0); + setup(); + + rs_restore_file(inf); + /* + * we do not close the file so that we will have a hold of the + * inode for as long as possible + */ + + if ( +#ifdef MASTER + !wizard && +#endif + md_unlink_open_file(file, inf) < 0) + { + printf("Cannot unlink file\n"); + return FALSE; + } + mpos = 0; +/* printw(0, 0, "%s: %s", file, ctime(&sbuf2.st_mtime)); */ +/* + printw("%s: %s", file, ctime(&sbuf2.st_mtime)); +*/ + clearok(stdscr,TRUE); + /* + * defeat multiple restarting from the same place + */ +#ifdef MASTER + if (!wizard) +#endif + if (sbuf2.st_nlink != 1 || syml) + { + endwin(); + printf("\nCannot restore from a linked file\n"); + return FALSE; + } + + if (pstats.s_hpt <= 0) + { + endwin(); + printf("\n\"He's dead, Jim\"\n"); + return FALSE; + } + + md_tstpresume(); + + environ = envp; + strcpy(file_name, file); + clearok(curscr, TRUE); + srand((int32_t)rs->seed);//md_getpid()); + msg(rs,"file name: %s", file); + playit(rs); + /*NOTREACHED*/ + return(0); +} + +/* + * encwrite: + * Perform an encrypted write + */ +#define CRYPT_ENABLE 0 + +size_t +encwrite(char *start, size_t size, FILE *outf) +{ + char *e1, *e2, fb; + int temp; + extern char *statlist; + size_t o_size = size; + e1 = encstr; + e2 = statlist; + fb = 0; + + while(size) + { + if ( CRYPT_ENABLE ) + { + if (putc(*start++ ^ *e1 ^ *e2 ^ fb, outf) == EOF) + break; + + temp = *e1++; + fb = fb + ((char) (temp * *e2++)); + if (*e1 == '\0') + e1 = encstr; + if (*e2 == '\0') + e2 = statlist; + } + else if ( putc(*start++,outf) == EOF ) + break; + size--; + } + + return(o_size - size); +} + +/* + * encread: + * Perform an encrypted read + */ +size_t +encread(char *start, size_t size, FILE *inf) +{ + char *e1, *e2, fb; + int temp; + size_t read_size; + extern char *statlist; + + fb = 0; + + if ((read_size = fread(start,1,size,inf)) == 0 || read_size == -1) + return(read_size); + if ( CRYPT_ENABLE ) + { + e1 = encstr; + e2 = statlist; + while (size--) + { + *start++ ^= *e1 ^ *e2 ^ fb; + temp = *e1++; + fb = fb + (char)(temp * *e2++); + if (*e1 == '\0') + e1 = encstr; + if (*e2 == '\0') + e2 = statlist; + } + } + return(read_size); +} + +static char scoreline[100]; +/* + * read_scrore + * Read in the score file + */ +void +rd_score(SCORE *top_ten) +{ + unsigned int i; + + if (scoreboard == NULL) + return; + + rewind(scoreboard); + + for(i = 0; i < numscores; i++) + { + encread(top_ten[i].sc_name, MAXSTR, scoreboard); + encread(scoreline, 100, scoreboard); + sscanf(scoreline, " %u %d %u %hu %d %x \n", + &top_ten[i].sc_uid, &top_ten[i].sc_score, + &top_ten[i].sc_flags, &top_ten[i].sc_monster, + &top_ten[i].sc_level, &top_ten[i].sc_time); + } + + rewind(scoreboard); +} + +/* + * write_scrore + * Read in the score file + */ +void +wr_score(SCORE *top_ten) +{ + unsigned int i; + + if (scoreboard == NULL) + return; + + rewind(scoreboard); + + for(i = 0; i < numscores; i++) + { + memset(scoreline,0,100); + encwrite(top_ten[i].sc_name, MAXSTR, scoreboard); + sprintf(scoreline, " %u %d %u %hu %d %x \n", + top_ten[i].sc_uid, top_ten[i].sc_score, + top_ten[i].sc_flags, top_ten[i].sc_monster, + top_ten[i].sc_level, top_ten[i].sc_time); + encwrite(scoreline,100,scoreboard); + } + + rewind(scoreboard); +} diff --git a/src/cc/rogue/score.h b/src/cc/rogue/score.h new file mode 100644 index 000000000..a5897b482 --- /dev/null +++ b/src/cc/rogue/score.h @@ -0,0 +1,29 @@ +/* + * Score file structure + * + * @(#)score.h 4.6 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ +#ifndef H_SCORE_H +#define H_SCORE_H + +struct sc_ent { + unsigned int sc_uid; + int sc_score; + unsigned int sc_flags; + unsigned short sc_monster; + char sc_name[MAXSTR]; + int sc_level; + unsigned int sc_time; +}; + +typedef struct sc_ent SCORE; + +void rd_score(SCORE *top_ten); +void wr_score(SCORE *top_ten); +#endif diff --git a/src/cc/rogue/scrolls.c b/src/cc/rogue/scrolls.c new file mode 100644 index 000000000..8ed6d2648 --- /dev/null +++ b/src/cc/rogue/scrolls.c @@ -0,0 +1,329 @@ +/* + * Read a scroll and let it happen + * + * @(#)scrolls.c 4.44 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +#include "rogue.h" + +/* + * read_scroll: + * Read a scroll from the pack and do the appropriate thing + */ + +void +read_scroll(struct rogue_state *rs) +{ + THING *obj; + PLACE *pp; + int y, x; + char ch; + int i; + bool discardit = FALSE; + struct room *cur_room; + THING *orig_obj; + static coord mp; + + obj = get_item(rs,"read", SCROLL); + if (obj == NULL) + return; + if (obj->o_type != SCROLL) + { + if (!terse) + msg(rs,"there is nothing on it to read"); + else + msg(rs,"nothing to read"); + return; + } + /* + * Calculate the effect it has on the poor guy. + */ + if (obj == cur_weapon) + cur_weapon = NULL; + /* + * Get rid of the thing + */ + discardit = (bool)(obj->o_count == 1); + leave_pack(rs,obj, FALSE, FALSE); + orig_obj = obj; + + switch (obj->o_which) + { + case S_CONFUSE: + /* + * Scroll of monster confusion. Give him that power. + */ + player.t_flags |= CANHUH; + msg(rs,"your hands begin to glow %s", pick_color("red")); + when S_ARMOR: + if (cur_armor != NULL) + { + cur_armor->o_arm--; + cur_armor->o_flags &= ~ISCURSED; + msg(rs,"your armor glows %s for a moment", pick_color("silver")); + } + when S_HOLD: + /* + * Hold monster scroll. Stop all monsters within two spaces + * from chasing after the hero. + */ + + ch = 0; + for (x = hero.x - 2; x <= hero.x + 2; x++) + if (x >= 0 && x < NUMCOLS) + for (y = hero.y - 2; y <= hero.y + 2; y++) + if (y >= 0 && y <= NUMLINES - 1) + if ((obj = moat(y, x)) != NULL && on(*obj, ISRUN)) + { + obj->t_flags &= ~ISRUN; + obj->t_flags |= ISHELD; + ch++; + } + if (ch) + { + addmsg(rs,"the monster"); + if (ch > 1) + addmsg(rs,"s around you"); + addmsg(rs," freeze"); + if (ch == 1) + addmsg(rs,"s"); + endmsg(rs); + scr_info[S_HOLD].oi_know = TRUE; + } + else + msg(rs,"you feel a strange sense of loss"); + when S_SLEEP: + /* + * Scroll which makes you fall asleep + */ + scr_info[S_SLEEP].oi_know = TRUE; + no_command += rnd(SLEEPTIME) + 4; + player.t_flags &= ~ISRUN; + msg(rs,"you fall asleep"); + when S_CREATE: + /* + * Create a monster: + * First look in a circle around him, next try his room + * otherwise give up + */ + i = 0; + for (y = hero.y - 1; y <= hero.y + 1; y++) + for (x = hero.x - 1; x <= hero.x + 1; x++) + /* + * Don't put a monster in top of the player. + */ + if (y == hero.y && x == hero.x) + continue; + /* + * Or anything else nasty + */ + else if (step_ok(ch = winat(y, x))) + { + if (ch == SCROLL + && find_obj(rs,y, x)->o_which == S_SCARE) + continue; + else if (rnd(++i) == 0) + { + mp.y = y; + mp.x = x; + } + } + if (i == 0) + msg(rs,"you hear a faint cry of anguish in the distance"); + else + { + obj = new_item(); + new_monster(rs,obj, randmonster(FALSE), &mp); + } + when S_ID_POTION: + case S_ID_SCROLL: + case S_ID_WEAPON: + case S_ID_ARMOR: + case S_ID_R_OR_S: + { + static char id_type[S_ID_R_OR_S + 1] = + { 0, 0, 0, 0, 0, POTION, SCROLL, WEAPON, ARMOR, R_OR_S }; + /* + * Identify, let him figure something out + */ + scr_info[obj->o_which].oi_know = TRUE; + msg(rs,"this scroll is an %s scroll", scr_info[obj->o_which].oi_name); + whatis(rs,TRUE, id_type[obj->o_which]); + } + when S_MAP: + /* + * Scroll of magic mapping. + */ + scr_info[S_MAP].oi_know = TRUE; + msg(rs,"oh, now this scroll has a map on it"); + /* + * take all the things we want to keep hidden out of the window + */ + for (y = 1; y < NUMLINES - 1; y++) + for (x = 0; x < NUMCOLS; x++) + { + pp = INDEX(y, x); + switch (ch = pp->p_ch) + { + case DOOR: + case STAIRS: + break; + + case '-': + case '|': + if (!(pp->p_flags & F_REAL)) + { + ch = pp->p_ch = DOOR; + pp->p_flags |= F_REAL; + } + break; + + case ' ': + if (pp->p_flags & F_REAL) + goto def; + pp->p_flags |= F_REAL; + ch = pp->p_ch = PASSAGE; + /* FALLTHROUGH */ + + case PASSAGE: +pass: + if (!(pp->p_flags & F_REAL)) + pp->p_ch = PASSAGE; + pp->p_flags |= (F_SEEN|F_REAL); + ch = PASSAGE; + break; + + case FLOOR: + if (pp->p_flags & F_REAL) + ch = ' '; + else + { + ch = TRAP; + pp->p_ch = TRAP; + pp->p_flags |= (F_SEEN|F_REAL); + } + break; + + default: +def: + if (pp->p_flags & F_PASS) + goto pass; + ch = ' '; + break; + } + if (ch != ' ') + { + if ((obj = pp->p_monst) != NULL) + obj->t_oldch = ch; + if (obj == NULL || !on(player, SEEMONST)) + mvaddch(y, x, ch); + } + } + when S_FDET: + /* + * Potion of gold detection + */ + ch = FALSE; + wclear(hw); + for (obj = lvl_obj; obj != NULL; obj = next(obj)) + if (obj->o_type == FOOD) + { + ch = TRUE; + wmove(hw, obj->o_pos.y, obj->o_pos.x); + waddch(hw, FOOD); + } + if (ch) + { + scr_info[S_FDET].oi_know = TRUE; + show_win(rs,"Your nose tingles and you smell food.--More--"); + } + else + msg(rs,"your nose tingles"); + when S_TELEP: + /* + * Scroll of teleportation: + * Make him dissapear and reappear + */ + { + cur_room = proom; + teleport(rs); + if (cur_room != proom) + scr_info[S_TELEP].oi_know = TRUE; + } + when S_ENCH: + if (cur_weapon == NULL || cur_weapon->o_type != WEAPON) + msg(rs,"you feel a strange sense of loss"); + else + { + cur_weapon->o_flags &= ~ISCURSED; + if (rnd(2) == 0) + cur_weapon->o_hplus++; + else + cur_weapon->o_dplus++; + msg(rs,"your %s glows %s for a moment", + weap_info[cur_weapon->o_which].oi_name, pick_color("blue")); + } + when S_SCARE: + /* + * Reading it is a mistake and produces laughter at her + * poor boo boo. + */ + msg(rs,"you hear maniacal laughter in the distance"); + when S_REMOVE: + uncurse(cur_armor); + uncurse(cur_weapon); + uncurse(cur_ring[LEFT]); + uncurse(cur_ring[RIGHT]); + msg(rs,choose_str("you feel in touch with the Universal Onenes", + "you feel as if somebody is watching over you")); + when S_AGGR: + /* + * This scroll aggravates all the monsters on the current + * level and sets them running towards the hero + */ + aggravate(rs); + msg(rs,"you hear a high pitched humming noise"); + when S_PROTECT: + if (cur_armor != NULL) + { + cur_armor->o_flags |= ISPROT; + msg(rs,"your armor is covered by a shimmering %s shield", + pick_color("gold")); + } + else + msg(rs,"you feel a strange sense of loss"); +#ifdef MASTER + otherwise: + msg(rs,"what a puzzling scroll!"); + return; +#endif + } + obj = orig_obj; + look(rs,TRUE); /* put the result of the scroll on the screen */ + status(rs); + + call_it(rs,&scr_info[obj->o_which]); + + if (discardit) + discard(obj); +} + +/* + * uncurse: + * Uncurse an item + */ + +void +uncurse(THING *obj) +{ + if (obj != NULL) + obj->o_flags &= ~ISCURSED; +} diff --git a/src/cc/rogue/state.c b/src/cc/rogue/state.c new file mode 100644 index 000000000..9963bbc9e --- /dev/null +++ b/src/cc/rogue/state.c @@ -0,0 +1,2275 @@ +/* + state.c - Portable Rogue Save State Code + + Copyright (C) 1999, 2000, 2005 Nicholas J. Kisseberth + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + 3. Neither the name(s) of the author(s) nor the names of other contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + SUCH DAMAGE. +*/ + +//#include +//#include +//#include +#include "rogue.h" + +/************************************************************************/ +/* Save State Code */ +/************************************************************************/ + +#define RSID_STATS 0xABCD0001 +#define RSID_THING 0xABCD0002 +#define RSID_THING_NULL 0xDEAD0002 +#define RSID_OBJECT 0xABCD0003 +#define RSID_MAGICITEMS 0xABCD0004 +#define RSID_KNOWS 0xABCD0005 +#define RSID_GUESSES 0xABCD0006 +#define RSID_OBJECTLIST 0xABCD0007 +#define RSID_BAGOBJECT 0xABCD0008 +#define RSID_MONSTERLIST 0xABCD0009 +#define RSID_MONSTERSTATS 0xABCD000A +#define RSID_MONSTERS 0xABCD000B +#define RSID_TRAP 0xABCD000C +#define RSID_WINDOW 0xABCD000D +#define RSID_DAEMONS 0xABCD000E +#define RSID_IWEAPS 0xABCD000F +#define RSID_IARMOR 0xABCD0010 +#define RSID_SPELLS 0xABCD0011 +#define RSID_ILIST 0xABCD0012 +#define RSID_HLIST 0xABCD0013 +#define RSID_DEATHTYPE 0xABCD0014 +#define RSID_CTYPES 0XABCD0015 +#define RSID_COORDLIST 0XABCD0016 +#define RSID_ROOMS 0XABCD0017 + +#define READSTAT (format_error || read_error ) +#define WRITESTAT (write_error) + +static int read_error = FALSE; +static int write_error = FALSE; +static int format_error = FALSE; +static int endian = 0x01020304; +#define big_endian ( *((char *)&endian) == 0x01 ) + +int +rs_write(FILE *savef, void *ptr, size_t size) +{ + if (write_error) + return(WRITESTAT); + + if (encwrite((char *)ptr, size, savef) != size) + write_error = 1; + + return(WRITESTAT); +} + +int +rs_read(FILE *inf, void *ptr, size_t size) +{ + if (read_error || format_error) + return(READSTAT); + + if (encread((char *)ptr, size, inf) != size) + read_error = 1; + + return(READSTAT); +} + +int +rs_write_int(FILE *savef, int32_t c) +{ + unsigned char bytes[4]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + rs_write(savef, buf, 4); + + return(WRITESTAT); +} + +int +rs_read_int(FILE *inf, int32_t *i) +{ + unsigned char bytes[4]; + int input = 0; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 4); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + *i = *((int *) buf); + + return(READSTAT); +} + +int +rs_write_char(FILE *savef, char c) +{ + if (write_error) + return(WRITESTAT); + + rs_write(savef, &c, 1); + + return(WRITESTAT); +} + +int +rs_read_char(FILE *inf, char *c) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, c, 1); + + return(READSTAT); +} + +int +rs_write_chars(FILE *savef, char *c, int count) +{ + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + rs_write(savef, c, count); + + return(WRITESTAT); +} + +int +rs_read_chars(FILE *inf, char *i, int count) +{ + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &value); + + if (value != count) + format_error = TRUE; + + rs_read(inf, i, count); + + return(READSTAT); +} + +int +rs_write_ints(FILE *savef, int32_t *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if( rs_write_int(savef,c[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_ints(FILE *inf, int32_t *i, int count) +{ + int n, value; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_int(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_boolean(FILE *savef, int c) +{ + unsigned char buf = (c == 0) ? 0 : 1; + + if (write_error) + return(WRITESTAT); + + rs_write(savef, &buf, 1); + + return(WRITESTAT); +} + +int +rs_read_boolean(FILE *inf, bool *i) +{ + unsigned char buf = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &buf, 1); + + *i = (buf != 0); + + return(READSTAT); +} + +int +rs_write_booleans(FILE *savef, bool *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if (rs_write_boolean(savef, c[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_booleans(FILE *inf, bool *i, int count) +{ + int n = 0, value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_boolean(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_short(FILE *savef, int16_t c) +{ + unsigned char bytes[2]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + rs_write(savef, buf, 2); + + return(WRITESTAT); +} + +int +rs_read_short(FILE *inf, int16_t *i) +{ + unsigned char bytes[2]; + short input; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 2); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + *i = *((short *) buf); + + return(READSTAT); +} + +int +rs_write_shorts(FILE *savef, int16_t *c, int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if (rs_write_short(savef, c[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_shorts(FILE *inf, int16_t *i, int count) +{ + int n = 0, value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < value; n++) + if (rs_read_short(inf, &i[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_ushort(FILE *savef, uint16_t c) +{ + unsigned char bytes[2]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + rs_write(savef, buf, 2); + + return(WRITESTAT); +} + +int +rs_read_ushort(FILE *inf, uint16_t *i) +{ + unsigned char bytes[2]; + unsigned short input; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 2); + + if (big_endian) + { + bytes[1] = buf[0]; + bytes[0] = buf[1]; + buf = bytes; + } + + *i = *((unsigned short *) buf); + + return(READSTAT); +} + +int +rs_write_uint(FILE *savef, uint32_t c) +{ + unsigned char bytes[4]; + unsigned char *buf = (unsigned char *) &c; + + if (write_error) + return(WRITESTAT); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + rs_write(savef, buf, 4); + + return(WRITESTAT); +} + +int +rs_read_uint(FILE *inf, uint32_t *i) +{ + unsigned char bytes[4]; + int input; + unsigned char *buf = (unsigned char *)&input; + + if (read_error || format_error) + return(READSTAT); + + rs_read(inf, &input, 4); + + if (big_endian) + { + bytes[3] = buf[0]; + bytes[2] = buf[1]; + bytes[1] = buf[2]; + bytes[0] = buf[3]; + buf = bytes; + } + + *i = *((unsigned int *) buf); + + return(READSTAT); +} + +int +rs_write_marker(FILE *savef, int32_t id) +{ + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, id); + + return(WRITESTAT); +} + +int +rs_read_marker(FILE *inf, int32_t id) +{ + int nid; + + if (read_error || format_error) + return(READSTAT); + + if (rs_read_int(inf, &nid) == 0) + if (id != nid) + format_error = 1; + + return(READSTAT); +} + + + +/******************************************************************************/ + +int +rs_write_string(FILE *savef, char *s) +{ + int len = 0; + + if (write_error) + return(WRITESTAT); + + len = (s == NULL) ? 0 : (int) strlen(s) + 1; + + rs_write_int(savef, len); + rs_write_chars(savef, s, len); + + return(WRITESTAT); +} + +int +rs_read_string(FILE *inf, char *s, int max) +{ + int len = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &len); + + if (len > max) + format_error = TRUE; + + rs_read_chars(inf, s, len); + + return(READSTAT); +} + +int +rs_read_new_string(FILE *inf, char **s) +{ + int len=0; + char *buf=0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &len); + + if (len == 0) + buf = NULL; + else + { + buf = (char *)malloc(len); + + if (buf == NULL) + read_error = TRUE; + } + + rs_read_chars(inf, buf, len); + + *s = buf; + + return(READSTAT); +} + +int +rs_write_strings(FILE *savef, char *s[], int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + if (rs_write_string(savef, s[n]) != 0) + break; + + return(WRITESTAT); +} + +int +rs_read_strings(FILE *inf, char **s, int count, int max) +{ + int n = 0; + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_string(inf, s[n], max) != 0) + break; + + return(READSTAT); +} + +int +rs_read_new_strings(FILE *inf, char **s, int count) +{ + int n = 0; + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &value); + + if (value != count) + format_error = TRUE; + + for(n = 0; n < count; n++) + if (rs_read_new_string(inf, &s[n]) != 0) + break; + + return(READSTAT); +} + +int +rs_write_string_index(FILE *savef, char *master[], int max, const char *str) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < max; i++) + if (str == master[i]) + return( rs_write_int(savef, i) ); + + return( rs_write_int(savef,-1) ); +} + +int +rs_read_string_index(FILE *inf, char *master[], int maxindex, char **str) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &i); + + if (i > maxindex) + format_error = TRUE; + else if (i >= 0) + *str = master[i]; + else + *str = NULL; + + return(READSTAT); +} + +int +rs_write_str_t(FILE *savef, str_t st) +{ + if (write_error) + return(WRITESTAT); + + rs_write_uint(savef, st); + + return( WRITESTAT ); +} + +int +rs_read_str_t(FILE *inf, str_t *st) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_uint(inf, st); + + return(READSTAT); +} + +int +rs_write_coord(FILE *savef, coord c) +{ + if (write_error) + return(WRITESTAT); + + rs_write_int(savef, c.x); + rs_write_int(savef, c.y); + + return(WRITESTAT); +} + +int +rs_read_coord(FILE *inf, coord *c) +{ + coord in; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&in.x); + rs_read_int(inf,&in.y); + + if (READSTAT == 0) + { + c->x = in.x; + c->y = in.y; + } + + return(READSTAT); +} + +int +rs_write_window(FILE *savef, WINDOW *win) +{ + int row,col,height,width; + + if (write_error) + return(WRITESTAT); + + width = getmaxx(win); + height = getmaxy(win); + + rs_write_marker(savef,RSID_WINDOW); + rs_write_int(savef,height); + rs_write_int(savef,width); + + for(row=0;rowl_next) + if (count == i) + return(l); + + return(NULL); +} + +int +find_list_ptr(THING *l, void *ptr) +{ + int count; + + for(count = 0; l != NULL; count++, l = l->l_next) + if (l == ptr) + return(count); + + return(-1); +} + +int +list_size(THING *l) +{ + int count; + + for(count = 0; l != NULL; count++, l = l->l_next) + ; + + return(count); +} + +/******************************************************************************/ + +int +rs_write_stats(FILE *savef, struct stats *s) +{ + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_STATS); + rs_write_str_t(savef, s->s_str); + rs_write_int(savef, s->s_exp); + rs_write_int(savef, s->s_lvl); + rs_write_int(savef, s->s_arm); + rs_write_int(savef, s->s_hpt); + rs_write_chars(savef, s->s_dmg, sizeof(s->s_dmg)); + rs_write_int(savef,s->s_maxhp); + + return(WRITESTAT); +} + +int +rs_read_stats(FILE *inf, struct stats *s) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_STATS); + rs_read_str_t(inf,&s->s_str); + rs_read_int(inf,&s->s_exp); + rs_read_int(inf,&s->s_lvl); + rs_read_int(inf,&s->s_arm); + rs_read_int(inf,&s->s_hpt); + rs_read_chars(inf,s->s_dmg,sizeof(s->s_dmg)); + rs_read_int(inf,&s->s_maxhp); + + return(READSTAT); +} + +int +rs_write_stone_index(FILE *savef, STONE master[], int max, const char *str) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < max; i++) + if (str == master[i].st_name) + { + rs_write_int(savef,i); + return(WRITESTAT); + } + + rs_write_int(savef,-1); + + return(WRITESTAT); +} + +int +rs_read_stone_index(FILE *inf, STONE master[], int maxindex, char **str) +{ + int i = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&i); + + if (i > maxindex) + format_error = TRUE; + else if (i >= 0) + *str = master[i].st_name; + else + *str = NULL; + + return(READSTAT); +} + +int +rs_write_scrolls(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < MAXSCROLLS; i++) + rs_write_string(savef, (char *)s_names[i]); + + return(READSTAT); +} + +int +rs_read_scrolls(FILE *inf) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXSCROLLS; i++) + rs_read_new_string(inf, (char **)&s_names[i]); + + return(READSTAT); +} + +int +rs_write_potions(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < MAXPOTIONS; i++) + rs_write_string_index(savef, (char **)rainbow, cNCOLORS, (char *)p_colors[i]); + + return(WRITESTAT); +} + +int +rs_read_potions(FILE *inf) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXPOTIONS; i++) + rs_read_string_index(inf, (char **)rainbow, cNCOLORS, (char **)&p_colors[i]); + + return(READSTAT); +} + +int +rs_write_rings(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < MAXRINGS; i++) + rs_write_stone_index(savef, (STONE *)stones, cNSTONES, (char *)r_stones[i]); + + return(WRITESTAT); +} + +int +rs_read_rings(FILE *inf) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXRINGS; i++) + rs_read_stone_index(inf, (STONE *)stones, cNSTONES, (char **)&r_stones[i]); + + return(READSTAT); +} + +int +rs_write_sticks(FILE *savef) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for (i = 0; i < MAXSTICKS; i++) + { + if (strcmp(ws_type[i],"staff") == 0) + { + rs_write_int(savef,0); + rs_write_string_index(savef, (char **)wood, cNWOOD, (char *)ws_made[i]); + } + else + { + rs_write_int(savef,1); + rs_write_string_index(savef, (char **)metal, cNMETAL, (char *)ws_made[i]); + } + } + + return(WRITESTAT); +} + +int +rs_read_sticks(FILE *inf) +{ + int i = 0, list = 0; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < MAXSTICKS; i++) + { + rs_read_int(inf,&list); + + if (list == 0) + { + rs_read_string_index(inf, (char **)wood, cNWOOD, (char **)&ws_made[i]); + ws_type[i] = "staff"; + } + else + { + rs_read_string_index(inf, (char **)metal, cNMETAL, (char **)&ws_made[i]); + ws_type[i] = "wand"; + } + } + + return(READSTAT); +} + +int +rs_write_daemons(FILE *savef, struct delayed_action *d_list, int count) +{ + int i = 0; + int func = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_DAEMONS); + rs_write_int(savef, count); + + for(i = 0; i < count; i++) + { + if (d_list[i].d_func == rollwand) + func = 1; + else if (d_list[i].d_func == doctor) + func = 2; + else if (d_list[i].d_func == stomach) + func = 3; + else if (d_list[i].d_func == runners) + func = 4; + else if (d_list[i].d_func == swander) + func = 5; + else if (d_list[i].d_func == nohaste) + func = 6; + else if (d_list[i].d_func == unconfuse) + func = 7; + else if (d_list[i].d_func == unsee) + func = 8; + else if (d_list[i].d_func == sight) + func = 9; + else if (d_list[i].d_func == NULL) + func = 0; + else + func = -1; + + rs_write_int(savef, d_list[i].d_type); + rs_write_int(savef, func); + rs_write_int(savef, d_list[i].d_arg); + rs_write_int(savef, d_list[i].d_time); + } + + return(WRITESTAT); +} + +int +rs_read_daemons(FILE *inf, struct delayed_action *d_list, int count) +{ + int i = 0; + int func = 0; + int value = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_DAEMONS); + rs_read_int(inf, &value); + + if (value > count) + format_error = TRUE; + + for(i=0; i < count; i++) + { + func = 0; + rs_read_int(inf, &d_list[i].d_type); + rs_read_int(inf, &func); + rs_read_int(inf, &d_list[i].d_arg); + rs_read_int(inf, &d_list[i].d_time); + + switch(func) + { + case 1: d_list[i].d_func = rollwand; + break; + case 2: d_list[i].d_func = doctor; + break; + case 3: d_list[i].d_func = stomach; + break; + case 4: d_list[i].d_func = runners; + break; + case 5: d_list[i].d_func = swander; + break; + case 6: d_list[i].d_func = nohaste; + break; + case 7: d_list[i].d_func = unconfuse; + break; + case 8: d_list[i].d_func = unsee; + break; + case 9: d_list[i].d_func = sight; + break; + default:d_list[i].d_func = NULL; + break; + } + } + + if (d_list[i].d_func == NULL) + { + d_list[i].d_type = 0; + d_list[i].d_arg = 0; + d_list[i].d_time = 0; + } + + return(READSTAT); +} + +int +rs_write_obj_info(FILE *savef, struct obj_info *i, int count) +{ + int n; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_MAGICITEMS); + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + { + /* mi_name is constant, defined at compile time in all cases */ + rs_write_int(savef,i[n].oi_prob); + rs_write_int(savef,i[n].oi_worth); + rs_write_string(savef,i[n].oi_guess); + rs_write_boolean(savef,i[n].oi_know); + } + + return(WRITESTAT); +} + +int +rs_read_obj_info(FILE *inf, struct obj_info *mi, int count) +{ + int n; + int value; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_MAGICITEMS); + + rs_read_int(inf, &value); + + if (value > count) + format_error = TRUE; + + for(n = 0; n < value; n++) + { + /* mi_name is const, defined at compile time in all cases */ + rs_read_int(inf,&mi[n].oi_prob); + rs_read_int(inf,&mi[n].oi_worth); + rs_read_new_string(inf,&mi[n].oi_guess); + rs_read_boolean(inf,&mi[n].oi_know); + } + + return(READSTAT); +} + +int +rs_write_room(FILE *savef, struct room *r) +{ + if (write_error) + return(WRITESTAT); + + rs_write_coord(savef, r->r_pos); + rs_write_coord(savef, r->r_max); + rs_write_coord(savef, r->r_gold); + rs_write_int(savef, r->r_goldval); + rs_write_short(savef, r->r_flags); + rs_write_int(savef, r->r_nexits); + rs_write_coord(savef, r->r_exit[0]); + rs_write_coord(savef, r->r_exit[1]); + rs_write_coord(savef, r->r_exit[2]); + rs_write_coord(savef, r->r_exit[3]); + rs_write_coord(savef, r->r_exit[4]); + rs_write_coord(savef, r->r_exit[5]); + rs_write_coord(savef, r->r_exit[6]); + rs_write_coord(savef, r->r_exit[7]); + rs_write_coord(savef, r->r_exit[8]); + rs_write_coord(savef, r->r_exit[9]); + rs_write_coord(savef, r->r_exit[10]); + rs_write_coord(savef, r->r_exit[11]); + + return(WRITESTAT); +} + +int +rs_read_room(FILE *inf, struct room *r) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_coord(inf,&r->r_pos); + rs_read_coord(inf,&r->r_max); + rs_read_coord(inf,&r->r_gold); + rs_read_int(inf,&r->r_goldval); + rs_read_short(inf,&r->r_flags); + rs_read_int(inf,&r->r_nexits); + rs_read_coord(inf,&r->r_exit[0]); + rs_read_coord(inf,&r->r_exit[1]); + rs_read_coord(inf,&r->r_exit[2]); + rs_read_coord(inf,&r->r_exit[3]); + rs_read_coord(inf,&r->r_exit[4]); + rs_read_coord(inf,&r->r_exit[5]); + rs_read_coord(inf,&r->r_exit[6]); + rs_read_coord(inf,&r->r_exit[7]); + rs_read_coord(inf,&r->r_exit[8]); + rs_read_coord(inf,&r->r_exit[9]); + rs_read_coord(inf,&r->r_exit[10]); + rs_read_coord(inf,&r->r_exit[11]); + + return(READSTAT); +} + +int +rs_write_rooms(FILE *savef, struct room r[], int count) +{ + int n = 0; + + if (write_error) + return(WRITESTAT); + //fprintf(stderr,"rooms %ld -> ",ftell(savef)); + rs_write_int(savef, count); + + for(n = 0; n < count; n++) + rs_write_room(savef, &r[n]); + //fprintf(stderr,"%ld\n",ftell(savef)); + + return(WRITESTAT); +} + +int +rs_read_rooms(FILE *inf, struct room *r, int count) +{ + int value = 0, n = 0; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf,&value); + + if (value > count) + format_error = TRUE; + + for(n = 0; n < value; n++) + rs_read_room(inf,&r[n]); + + return(READSTAT); +} + +int +rs_write_room_reference(FILE *savef, struct room *rp) +{ + int i, room = -1; + + if (write_error) + return(WRITESTAT); + + for (i = 0; i < MAXROOMS; i++) + if (&rooms[i] == rp) + room = i; + + rs_write_int(savef, room); + + return(WRITESTAT); +} + +int +rs_read_room_reference(FILE *inf, struct room **rp) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &i); + + *rp = &rooms[i]; + + return(READSTAT); +} + +int +rs_write_monsters(FILE *savef, struct monster *m, int count) +{ + int n; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_MONSTERS); + rs_write_int(savef, count); + + for(n=0;ntype != AMULET ) + { + o->_o._o_type = item->type; + o->_o._o_launch = item->launch; + memcpy(o->_o._o_damage,item->damage,sizeof(item->damage)); + memcpy(o->_o._o_hurldmg,item->hurldmg,sizeof(item->hurldmg)); + o->_o._o_count = item->count; + o->_o._o_which = item->which; + o->_o._o_hplus = item->hplus; + o->_o._o_dplus = item->dplus; + o->_o._o_arm = item->arm; + o->_o._o_flags = item->flags; + o->_o._o_group = item->group; + o->o_flags |= ISKNOW; + o->o_flags &= ~ISFOUND; + switch ( item->type ) + { + case SCROLL: + if ( item->which < MAXSCROLLS ) + scr_info[item->which].oi_know = TRUE; + break; + case POTION: + if ( item->which < MAXPOTIONS ) + pot_info[item->which].oi_know = TRUE; + break; + case RING: + if ( item->which < MAXRINGS ) + ring_info[item->which].oi_know = TRUE; + break; + case STICK: + if ( item->which < MAXSTICKS ) + ws_info[item->which].oi_know = TRUE; + break; + // cur_armor and cur_weapon should be set + } + //char packitemstr[256]; + //strcpy(packitemstr,inv_name(o,FALSE)); + //fprintf(stderr,"packitem.(%s)\n",packitemstr); + } +} + +void rogue_packitemstr(char *packitemstr,struct rogue_packitem *item) +{ + static int32_t didinit; int32_t i; + if ( didinit == 0 ) + { + struct rogue_state R; char keystrokes[3]; + memset(&R,0,sizeof(R)); + keystrokes[0] = 'Q'; + keystrokes[1] = 'y'; + keystrokes[2] = 0; + R.keystrokes = keystrokes; + R.numkeys = 2; + rogueiterate(&R); + didinit = 1; + } + THING *obj = new_item(); + rogue_restoreobject(obj,item); + strcpy(packitemstr,inv_name(obj,FALSE)); + //fprintf(stderr,"packitem.(%s)\n",packitemstr); + free(obj); +} + +int32_t packsave(struct rogue_packitem *item,int32_t type,int32_t launch,char *damage,int32_t damagesize,char *hurldmg,int32_t hurlsize,int32_t count,int32_t which,int32_t hplus,int32_t dplus,int32_t arm,int32_t flags,int32_t group) +{ + if ( damagesize != 8 || hurlsize != 8 ) + { + fprintf(stderr,"unexpected damagesize.%d or hurlsize.%d != 8\n",damagesize,hurlsize); + return(-1); + } + item->type = type; + item->launch = launch; + memcpy(item->damage,damage,damagesize); + memcpy(item->hurldmg,hurldmg,hurlsize); + item->count = count; + item->which = which; + item->hplus = hplus; + item->dplus = dplus; + item->arm = arm; + item->flags = flags; + item->group = group; + return(0); +} + +int +rs_write_object(struct rogue_state *rs,FILE *savef, THING *o) +{ + struct rogue_packitem *item; + if (write_error) + return(WRITESTAT); + if ( thing_find(o) < 0 ) + { + fprintf(stderr,"cant find thing.%p (%s) in list\n",o,inv_name(o,FALSE)); //sleep(3); + return(0); + } + if ( o->_o._o_packch != 0 ) + { + item = &rs->P.roguepack[rs->P.packsize]; + if ( 1 && pstats.s_hpt <= 0 ) + { + //fprintf(stderr,"KILLED\n"); + rs->P.gold = -1; + rs->P.hitpoints = -1; + rs->P.strength = 0; + rs->P.level = -1; + rs->P.experience = -1; + rs->P.dungeonlevel = -1; + } + else + { + if ( rs->P.packsize == 0 ) + { + rs->P.gold = purse; + rs->P.hitpoints = max_hp; + rs->P.strength = (pstats.s_str & 0xffff) | (max_stats.s_str << 16); + rs->P.level = pstats.s_lvl; + rs->P.experience = pstats.s_exp; + rs->P.dungeonlevel = level; + rs->P.amulet = amulet; + //fprintf(stderr,"%ld gold.%d hp.%d strength.%d/%d level.%d exp.%d %d\n",ftell(savef),purse,max_hp,pstats.s_str,max_stats.s_str,pstats.s_lvl,pstats.s_exp,level); + } + //fprintf(stderr,"object (%s) x.%d y.%d type.%d pack.(%c:%d)\n",inv_name(o,FALSE),o->_o._o_pos.x,o->_o._o_pos.y,o->_o._o_type,o->_o._o_packch,o->_o._o_packch); + if ( rs->P.packsize < MAXPACK && o->o_type != AMULET ) + { + packsave(item,o->_o._o_type,o->_o._o_launch,o->_o._o_damage,sizeof(o->_o._o_damage),o->_o._o_hurldmg,sizeof(o->_o._o_hurldmg),o->_o._o_count,o->_o._o_which,o->_o._o_hplus,o->_o._o_dplus,o->_o._o_arm,o->_o._o_flags,o->_o._o_group); + rs->P.packsize++; + } + } + } + rs_write_marker(savef, RSID_OBJECT); + rs_write_int(savef, o->_o._o_type); + rs_write_coord(savef, o->_o._o_pos); + rs_write_int(savef, o->_o._o_launch); + rs_write_char(savef, o->_o._o_packch); + rs_write_chars(savef, o->_o._o_damage, sizeof(o->_o._o_damage)); + rs_write_chars(savef, o->_o._o_hurldmg, sizeof(o->_o._o_hurldmg)); + rs_write_int(savef, o->_o._o_count); + rs_write_int(savef, o->_o._o_which); + rs_write_int(savef, o->_o._o_hplus); + rs_write_int(savef, o->_o._o_dplus); + rs_write_int(savef, o->_o._o_arm); + rs_write_int(savef, o->_o._o_flags); + rs_write_int(savef, o->_o._o_group); + rs_write_string(savef, o->_o._o_label); + return(WRITESTAT); +} + +int +rs_read_object(FILE *inf, THING *o) +{ + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_OBJECT); + rs_read_int(inf, &o->_o._o_type); + rs_read_coord(inf, &o->_o._o_pos); + rs_read_int(inf, &o->_o._o_launch); + rs_read_char(inf, &o->_o._o_packch); + rs_read_chars(inf, o->_o._o_damage, sizeof(o->_o._o_damage)); + rs_read_chars(inf, o->_o._o_hurldmg, sizeof(o->_o._o_hurldmg)); + rs_read_int(inf, &o->_o._o_count); + rs_read_int(inf, &o->_o._o_which); + rs_read_int(inf, &o->_o._o_hplus); + rs_read_int(inf, &o->_o._o_dplus); + rs_read_int(inf, &o->_o._o_arm); + rs_read_int(inf, &o->_o._o_flags); + rs_read_int(inf, &o->_o._o_group); + rs_read_new_string(inf, &o->_o._o_label); + + return(READSTAT); +} + +int +rs_write_object_list(struct rogue_state *rs,FILE *savef, THING *l) +{ + if (write_error) + return(WRITESTAT); + //fprintf(stderr,"list %ld -> ",ftell(savef)); + + rs_write_marker(savef, RSID_OBJECTLIST); + rs_write_int(savef, list_size(l)); + + for( ;l != NULL; l = l->l_next) + rs_write_object(rs,savef, l); + //fprintf(stderr,"%ld\n",ftell(savef)); + + return(WRITESTAT); +} + +int +rs_read_object_list(FILE *inf, THING **list) +{ + int i, cnt; + THING *l = NULL, *previous = NULL, *head = NULL; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_OBJECTLIST); + rs_read_int(inf, &cnt); + + for (i = 0; i < cnt; i++) + { + l = new_item();//,sizeof(THING)); + + memset(l,0,sizeof(THING)); + + l->l_prev = previous; + + if (previous != NULL) + previous->l_next = l; + + rs_read_object(inf,l); + + if (previous == NULL) + head = l; + + previous = l; + } + + if (l != NULL) + l->l_next = NULL; + + *list = head; + + return(READSTAT); +} + +int +rs_write_object_reference(FILE *savef, THING *list, THING *item) +{ + int i; + + if (write_error) + return(WRITESTAT); + + i = find_list_ptr(list, item); + + rs_write_int(savef, i); + + return(WRITESTAT); +} + +int +rs_read_object_reference(FILE *inf, THING *list, THING **item) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &i); + + *item = (THING *)get_list_item(list,i); + + return(READSTAT); +} + +int +find_room_coord(struct room *rmlist, coord *c, int n) +{ + int i = 0; + + for(i = 0; i < n; i++) + if(&rmlist[i].r_gold == c) + return(i); + + return(-1); +} + +int +find_thing_coord(THING *monlist, coord *c) +{ + THING *mitem; + THING *tp; + int i = 0; + + for(mitem = monlist; mitem != NULL; mitem = mitem->l_next) + { + tp = mitem; + + if (c == &tp->t_pos) + return(i); + + i++; + } + + return(-1); +} + +int +find_object_coord(THING *objlist, coord *c) +{ + THING *oitem; + THING *obj; + int i = 0; + + for(oitem = objlist; oitem != NULL; oitem = oitem->l_next) + { + obj = oitem; + + if (c == &obj->o_pos) + return(i); + + i++; + } + + return(-1); +} + +int +rs_write_thing(struct rogue_state *rs,FILE *savef, THING *t) +{ + int i = -1; + + if (write_error) + return(WRITESTAT); + //fprintf(stderr,"thing %ld -> ",ftell(savef)); + + rs_write_marker(savef, RSID_THING); + + if (t == NULL) + { + rs_write_int(savef, 0); + return(WRITESTAT); + } + + rs_write_int(savef, 1); + rs_write_coord(savef, t->_t._t_pos); + rs_write_boolean(savef, t->_t._t_turn); + rs_write_char(savef, t->_t._t_type); + rs_write_char(savef, t->_t._t_disguise); + rs_write_char(savef, t->_t._t_oldch); + + /* + t_dest can be: + 0,0: NULL + 0,1: location of hero + 1,i: location of a thing (monster) + 2,i: location of an object + 3,i: location of gold in a room + + We need to remember what we are chasing rather than + the current location of what we are chasing. + */ + + if (t->t_dest == &hero) + { + rs_write_int(savef,0); + rs_write_int(savef,1); + } + else if (t->t_dest != NULL) + { + i = find_thing_coord(mlist, t->t_dest); + + if (i >=0 ) + { + rs_write_int(savef,1); + rs_write_int(savef,i); + } + else + { + i = find_object_coord(lvl_obj, t->t_dest); + + if (i >= 0) + { + rs_write_int(savef,2); + rs_write_int(savef,i); + } + else + { + i = find_room_coord(rooms, t->t_dest, MAXROOMS); + + if (i >= 0) + { + rs_write_int(savef,3); + rs_write_int(savef,i); + } + else + { + rs_write_int(savef, 0); + rs_write_int(savef,1); /* chase the hero anyway */ + } + } + } + } + else + { + rs_write_int(savef,0); + rs_write_int(savef,0); + } + + rs_write_short(savef, t->_t._t_flags); + rs_write_stats(savef, &t->_t._t_stats); + rs_write_room_reference(savef, t->_t._t_room); + rs_write_object_list(rs,savef, t->_t._t_pack); + //fprintf(stderr,"%ld\n",ftell(savef)); + + return(WRITESTAT); +} + +int +rs_read_thing(FILE *inf, THING *t) +{ + int listid = 0, index = -1; + THING *item; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_THING); + + rs_read_int(inf, &index); + + if (index == 0) + return(READSTAT); + + rs_read_coord(inf,&t->_t._t_pos); + rs_read_boolean(inf,&t->_t._t_turn); + rs_read_char(inf,&t->_t._t_type); + rs_read_char(inf,&t->_t._t_disguise); + rs_read_char(inf,&t->_t._t_oldch); + + /* + t_dest can be (listid,index): + 0,0: NULL + 0,1: location of hero + 1,i: location of a thing (monster) + 2,i: location of an object + 3,i: location of gold in a room + + We need to remember what we are chasing rather than + the current location of what we are chasing. + */ + + rs_read_int(inf, &listid); + rs_read_int(inf, &index); + t->_t._t_reserved = -1; + + if (listid == 0) /* hero or NULL */ + { + if (index == 1) + t->_t._t_dest = &hero; + else + t->_t._t_dest = NULL; + } + else if (listid == 1) /* monster/thing */ + { + t->_t._t_dest = NULL; + t->_t._t_reserved = index; + } + else if (listid == 2) /* object */ + { + THING *obj; + + item = (THING *)get_list_item(lvl_obj, index); + + if (item != NULL) + { + obj = item; + t->_t._t_dest = &obj->o_pos; + } + } + else if (listid == 3) /* gold */ + { + t->_t._t_dest = &rooms[index].r_gold; + } + else + t->_t._t_dest = NULL; + + rs_read_short(inf,&t->_t._t_flags); + rs_read_stats(inf,&t->_t._t_stats); + rs_read_room_reference(inf, &t->_t._t_room); + rs_read_object_list(inf,&t->_t._t_pack); + + return(READSTAT); +} + +void +rs_fix_thing(THING *t) +{ + THING *item; + THING *tp; + + if (t->t_reserved < 0) + return; + + item = (THING *)get_list_item(mlist,t->t_reserved); + + if (item != NULL) + { + tp = item; + t->t_dest = &tp->t_pos; + } +} + +int +rs_write_thing_list(struct rogue_state *rs,FILE *savef, THING *l) +{ + int cnt = 0; + + if (write_error) + return(WRITESTAT); + + rs_write_marker(savef, RSID_MONSTERLIST); + + cnt = list_size(l); + + rs_write_int(savef, cnt); + + if (cnt < 1) + return(WRITESTAT); + + while (l != NULL) { + rs_write_thing(rs,savef, l); + l = l->l_next; + } + + return(WRITESTAT); +} + +int +rs_read_thing_list(FILE *inf, THING **list) +{ + int i, cnt; + THING *l = NULL, *previous = NULL, *head = NULL; + + if (read_error || format_error) + return(READSTAT); + + rs_read_marker(inf, RSID_MONSTERLIST); + + rs_read_int(inf, &cnt); + + for (i = 0; i < cnt; i++) + { + l = new_item(); + + l->l_prev = previous; + + if (previous != NULL) + previous->l_next = l; + + rs_read_thing(inf,l); + + if (previous == NULL) + head = l; + + previous = l; + } + + if (l != NULL) + l->l_next = NULL; + + *list = head; + + return(READSTAT); +} + +void +rs_fix_thing_list(THING *list) +{ + THING *item; + + for(item = list; item != NULL; item = item->l_next) + rs_fix_thing(item); +} + +int +rs_write_thing_reference(FILE *savef, THING *list, THING *item) +{ + int i; + + if (write_error) + return(WRITESTAT); + + if (item == NULL) + rs_write_int(savef,-1); + else + { + i = find_list_ptr(list, item); + + rs_write_int(savef, i); + } + + return(WRITESTAT); +} + +int +rs_read_thing_reference(FILE *inf, THING *list, THING **item) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + rs_read_int(inf, &i); + + if (i == -1) + *item = NULL; + else + *item = (THING *)get_list_item(list,i); + + return(READSTAT); +} + +int +rs_write_thing_references(FILE *savef, THING *list, THING *items[], int count) +{ + int i; + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < count; i++) + rs_write_thing_reference(savef,list,items[i]); + + return(WRITESTAT); +} + +int +rs_read_thing_references(FILE *inf, THING *list, THING *items[], int count) +{ + int i; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < count; i++) + rs_read_thing_reference(inf,list,&items[i]); + + return(WRITESTAT); +} + +int +rs_write_places(FILE *savef, PLACE *places, int count) +{ + int i = 0; + //fprintf(stderr,"places %ld -> ",ftell(savef)); + + if (write_error) + return(WRITESTAT); + + for(i = 0; i < count; i++) + { + rs_write_char(savef, places[i].p_ch); + rs_write_char(savef, places[i].p_flags); + rs_write_thing_reference(savef, mlist, places[i].p_monst); + } + //fprintf(stderr,"%ld\n",ftell(savef)); + + return(WRITESTAT); +} + +int +rs_read_places(FILE *inf, PLACE *places, int count) +{ + int i = 0; + + if (read_error || format_error) + return(READSTAT); + + for(i = 0; i < count; i++) + { + rs_read_char(inf,&places[i].p_ch); + rs_read_char(inf,&places[i].p_flags); + rs_read_thing_reference(inf, mlist, &places[i].p_monst); + } + + return(READSTAT); +} + +int +rs_save_file(struct rogue_state *rs,FILE *savef) +{ + if (write_error) + { + fprintf(stderr,"write error\n"); + return(WRITESTAT); + } + + rs_write_boolean(savef, after); /* 1 */ /* extern.c */ + rs_write_boolean(savef, again); /* 2 */ + rs_write_int(savef, noscore); /* 3 */ + rs_write_boolean(savef, seenstairs); /* 4 */ + rs_write_boolean(savef, amulet); /* 5 */ + rs_write_boolean(savef, door_stop); /* 6 */ + rs_write_boolean(savef, fight_flush); /* 7 */ + rs_write_boolean(savef, firstmove); /* 8 */ + rs_write_boolean(savef, got_ltc); /* 9 */ + rs_write_boolean(savef, has_hit); /* 10 */ + rs_write_boolean(savef, in_shell); /* 11 */ + rs_write_boolean(savef, inv_describe); /* 12 */ + rs_write_boolean(savef, jump); /* 13 */ + rs_write_boolean(savef, kamikaze); /* 14 */ + rs_write_boolean(savef, lower_msg); /* 15 */ + rs_write_boolean(savef, move_on); /* 16 */ + rs_write_boolean(savef, msg_esc); /* 17 */ + rs_write_boolean(savef, passgo); /* 18 */ + rs_write_boolean(savef, playing); /* 19 */ + rs_write_boolean(savef, q_comm); /* 20 */ + rs_write_boolean(savef, running); /* 21 */ + rs_write_boolean(savef, save_msg); /* 22 */ + rs_write_boolean(savef, see_floor); /* 23 */ + rs_write_boolean(savef, stat_msg); /* 24 */ + rs_write_boolean(savef, terse); /* 25 */ + rs_write_boolean(savef, to_death); /* 26 */ + rs_write_boolean(savef, tombstone); /* 27 */ +#ifdef MASTER + rs_write_int(savef, wizard); /* 28 */ +#else + rs_write_int(savef, 0); /* 28 */ +#endif + rs_write_booleans(savef, pack_used, 26); /* 29 */ + rs_write_char(savef, dir_ch); + //rs_write_chars(savef, file_name, MAXSTR); + //rs_write_chars(savef, huh, MAXSTR); + rs_write_potions(savef); + //rs_write_chars(savef,prbuf,2*MAXSTR); + rs_write_rings(savef); + rs_write_string(savef,release); + rs_write_char(savef, runch); + rs_write_scrolls(savef); + rs_write_char(savef, take); + //rs_write_chars(savef, whoami, MAXSTR); + rs_write_sticks(savef); + rs_write_int(savef,orig_dsusp); + rs_write_chars(savef, fruit, MAXSTR); + //rs_write_chars(savef, home, MAXSTR); + rs_write_strings(savef,(char **)inv_t_name,3); + rs_write_char(savef,l_last_comm); + rs_write_char(savef,l_last_dir); + rs_write_char(savef,last_comm); + rs_write_char(savef,last_dir); + rs_write_strings(savef,(char **)tr_name,8); + rs_write_int(savef,n_objs); + rs_write_int(savef, ntraps); + rs_write_int(savef, hungry_state); + rs_write_int(savef, inpack); + rs_write_int(savef, inv_type); + rs_write_int(savef, level); + rs_write_int(savef, max_level); + rs_write_int(savef, mpos); + rs_write_int(savef, no_food); + rs_write_ints(savef,(int32_t *)a_class,MAXARMORS); + rs_write_int(savef, count); + rs_write_int(savef, food_left); + rs_write_int(savef, lastscore); + rs_write_int(savef, no_command); + rs_write_int(savef, no_move); + rs_write_int(savef, purse); + rs_write_int(savef, quiet); + rs_write_int(savef, vf_hit); + //rs_write_int(savef, dnum); + rs_write_int(savef, (int32_t)(seed&0xffffffff)); + rs_write_int(savef, (int32_t)((seed>>32)&0xffffffff)); + rs_write_ints(savef, (int32_t *)e_levels, 21); + rs_write_coord(savef, delta); + rs_write_coord(savef, oldpos); + rs_write_coord(savef, stairs); + rs_write_thing(rs,savef, &player); + rs_write_object_reference(savef, player.t_pack, cur_armor); + rs_write_object_reference(savef, player.t_pack, cur_ring[0]); + rs_write_object_reference(savef, player.t_pack, cur_ring[1]); + rs_write_object_reference(savef, player.t_pack, cur_weapon); + rs_write_object_reference(savef, player.t_pack, l_last_pick); + rs_write_object_reference(savef, player.t_pack, last_pick); + + rs_write_object_list(rs,savef, lvl_obj); + rs_write_thing_list(rs,savef, mlist); + + rs_write_places(savef,places,MAXLINES*MAXCOLS); + + rs_write_stats(savef,&max_stats); + rs_write_rooms(savef, rooms, MAXROOMS); + rs_write_room_reference(savef, oldrp); + rs_write_rooms(savef, passages, MAXPASS); + + rs_write_monsters(savef,monsters,26); + rs_write_obj_info(savef, things, NUMTHINGS); + rs_write_obj_info(savef, arm_info, MAXARMORS); + rs_write_obj_info(savef, pot_info, MAXPOTIONS); + rs_write_obj_info(savef, ring_info, MAXRINGS); + rs_write_obj_info(savef, scr_info, MAXSCROLLS); + rs_write_obj_info(savef, weap_info, MAXWEAPONS+1); + rs_write_obj_info(savef, ws_info, MAXSTICKS); + + + rs_write_daemons(savef, &d_list[0], 20); /* 5.4-daemon.c */ +#ifdef MASTER + rs_write_int(savef,total); /* 5.4-list.c */ +#else + rs_write_int(savef, 0); +#endif + rs_write_int(savef,between); /* 5.4-daemons.c*/ + rs_write_coord(savef, nh); /* 5.4-move.c */ + rs_write_int(savef, group); /* 5.4-weapons.c */ + //fprintf(stderr,"fifth %ld\n",ftell(savef)); + + rs_write_window(savef,stdscr); + //fprintf(stderr,"done %ld\n",ftell(savef)); + + return(WRITESTAT); +} + +int +rs_restore_file(FILE *inf) +{ + int dummyint; + + if (read_error || format_error) + return(READSTAT); + + rs_read_boolean(inf, &after); /* 1 */ /* extern.c */ + rs_read_boolean(inf, &again); /* 2 */ + rs_read_int(inf, &noscore); /* 3 */ + rs_read_boolean(inf, &seenstairs); /* 4 */ + rs_read_boolean(inf, &amulet); /* 5 */ + rs_read_boolean(inf, &door_stop); /* 6 */ + rs_read_boolean(inf, &fight_flush); /* 7 */ + rs_read_boolean(inf, &firstmove); /* 8 */ + rs_read_boolean(inf, &got_ltc); /* 9 */ + rs_read_boolean(inf, &has_hit); /* 10 */ + rs_read_boolean(inf, &in_shell); /* 11 */ + rs_read_boolean(inf, &inv_describe); /* 12 */ + rs_read_boolean(inf, &jump); /* 13 */ + rs_read_boolean(inf, &kamikaze); /* 14 */ + rs_read_boolean(inf, &lower_msg); /* 15 */ + rs_read_boolean(inf, &move_on); /* 16 */ + rs_read_boolean(inf, &msg_esc); /* 17 */ + rs_read_boolean(inf, &passgo); /* 18 */ + rs_read_boolean(inf, &playing); /* 19 */ + rs_read_boolean(inf, &q_comm); /* 20 */ + rs_read_boolean(inf, &running); /* 21 */ + rs_read_boolean(inf, &save_msg); /* 22 */ + rs_read_boolean(inf, &see_floor); /* 23 */ + rs_read_boolean(inf, &stat_msg); /* 24 */ + rs_read_boolean(inf, &terse); /* 25 */ + rs_read_boolean(inf, &to_death); /* 26 */ + rs_read_boolean(inf, &tombstone); /* 27 */ +#ifdef MASTER + rs_read_int(inf, &wizard); /* 28 */ +#else + rs_read_int(inf, &dummyint); /* 28 */ +#endif + rs_read_booleans(inf, pack_used, 26); /* 29 */ + rs_read_char(inf, &dir_ch); + //rs_read_chars(inf, file_name, MAXSTR); + //rs_read_chars(inf, huh, MAXSTR); + rs_read_potions(inf); + //rs_read_chars(inf, prbuf, 2*MAXSTR); + rs_read_rings(inf); + rs_read_new_string(inf,&release); + rs_read_char(inf, &runch); + rs_read_scrolls(inf); + rs_read_char(inf, &take); + //rs_read_chars(inf, whoami, MAXSTR); + rs_read_sticks(inf); + rs_read_int(inf,&orig_dsusp); + rs_read_chars(inf, fruit, MAXSTR); + //rs_read_chars(inf, home, MAXSTR); + rs_read_new_strings(inf,(char **)inv_t_name,3); + rs_read_char(inf, &l_last_comm); + rs_read_char(inf, &l_last_dir); + rs_read_char(inf, &last_comm); + rs_read_char(inf, &last_dir); + rs_read_new_strings(inf,(char **)tr_name,8); + rs_read_int(inf, &n_objs); + rs_read_int(inf, &ntraps); + rs_read_int(inf, &hungry_state); + rs_read_int(inf, &inpack); + rs_read_int(inf, &inv_type); + rs_read_int(inf, &level); + rs_read_int(inf, &max_level); + rs_read_int(inf, &mpos); + rs_read_int(inf, &no_food); + rs_read_ints(inf,(int32_t *)a_class,MAXARMORS); + rs_read_int(inf, &count); + rs_read_int(inf, &food_left); + rs_read_int(inf, &lastscore); + rs_read_int(inf, &no_command); + rs_read_int(inf, &no_move); + rs_read_int(inf, &purse); + rs_read_int(inf, &quiet); + rs_read_int(inf, &vf_hit); + //rs_read_int(inf, &dnum); + int32_t lownum,highnum; + rs_read_int(inf, &lownum); + rs_read_int(inf, &highnum); + seed = ((uint64_t)highnum<<32) | (lownum&0xffffffff); + rs_read_ints(inf,(int32_t *)e_levels,21); + rs_read_coord(inf, &delta); + rs_read_coord(inf, &oldpos); + rs_read_coord(inf, &stairs); + + rs_read_thing(inf, &player); + rs_read_object_reference(inf, player.t_pack, &cur_armor); + rs_read_object_reference(inf, player.t_pack, &cur_ring[0]); + rs_read_object_reference(inf, player.t_pack, &cur_ring[1]); + rs_read_object_reference(inf, player.t_pack, &cur_weapon); + rs_read_object_reference(inf, player.t_pack, &l_last_pick); + rs_read_object_reference(inf, player.t_pack, &last_pick); + + rs_read_object_list(inf, &lvl_obj); + rs_read_thing_list(inf, &mlist); + rs_fix_thing(&player); + rs_fix_thing_list(mlist); + + rs_read_places(inf,places,MAXLINES*MAXCOLS); + + rs_read_stats(inf, &max_stats); + rs_read_rooms(inf, rooms, MAXROOMS); + rs_read_room_reference(inf, &oldrp); + rs_read_rooms(inf, passages, MAXPASS); + + rs_read_monsters(inf,monsters,26); + rs_read_obj_info(inf, things, NUMTHINGS); + rs_read_obj_info(inf, arm_info, MAXARMORS); + rs_read_obj_info(inf, pot_info, MAXPOTIONS); + rs_read_obj_info(inf, ring_info, MAXRINGS); + rs_read_obj_info(inf, scr_info, MAXSCROLLS); + rs_read_obj_info(inf, weap_info, MAXWEAPONS+1); + rs_read_obj_info(inf, ws_info, MAXSTICKS); + + rs_read_daemons(inf, d_list, 20); /* 5.4-daemon.c */ + rs_read_int(inf,&dummyint); /* total */ /* 5.4-list.c */ + rs_read_int(inf,&between); /* 5.4-daemons.c */ + rs_read_coord(inf, &nh); /* 5.4-move.c */ + rs_read_int(inf,&group); /* 5.4-weapons.c */ + + rs_read_window(inf,stdscr); + + return(READSTAT); +} diff --git a/src/cc/rogue/sticks.c b/src/cc/rogue/sticks.c new file mode 100644 index 000000000..cd559daef --- /dev/null +++ b/src/cc/rogue/sticks.c @@ -0,0 +1,432 @@ +/* + * Functions to implement the various sticks one might find + * while wandering around the dungeon. + * + * @(#)sticks.c 4.39 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +#include "rogue.h" + +/* + * fix_stick: + * Set up a new stick + */ + +void +fix_stick(THING *cur) +{ + if (strcmp(ws_type[cur->o_which], "staff") == 0) + strncpy(cur->o_damage,"2x3",sizeof(cur->o_damage)); + else + strncpy(cur->o_damage,"1x1",sizeof(cur->o_damage)); + strncpy(cur->o_hurldmg,"1x1",sizeof(cur->o_hurldmg)); + + switch (cur->o_which) + { + case WS_LIGHT: + cur->o_charges = rnd(10) + 10; + otherwise: + cur->o_charges = rnd(5) + 3; + } +} + +/* + * do_zap: + * Perform a zap with a wand + */ + +void +do_zap(struct rogue_state *rs) +{ + THING *obj, *tp; + int y, x; + char *name; + char monster, oldch; + static THING bolt; + + if ((obj = get_item(rs,"zap with", STICK)) == NULL) + return; + if (obj->o_type != STICK) + { + after = FALSE; + msg(rs,"you can't zap with that!"); + return; + } + if (obj->o_charges == 0) + { + msg(rs,"nothing happens"); + return; + } + switch (obj->o_which) + { + case WS_LIGHT: + /* + * Reddy Kilowat wand. Light up the room + */ + ws_info[WS_LIGHT].oi_know = TRUE; + if (proom->r_flags & ISGONE) + msg(rs,"the corridor glows and then fades"); + else + { + proom->r_flags &= ~ISDARK; + /* + * Light the room and put the player back up + */ + enter_room(rs,&hero); + addmsg(rs,"the room is lit"); + if (!terse) + addmsg(rs," by a shimmering %s light", pick_color("blue")); + endmsg(rs); + } + when WS_DRAIN: + /* + * take away 1/2 of hero's hit points, then take it away + * evenly from the monsters in the room (or next to hero + * if he is in a passage) + */ + if (pstats.s_hpt < 2) + { + msg(rs,"you are too weak to use it"); + return; + } + else + drain(rs); + when WS_INVIS: + case WS_POLYMORPH: + case WS_TELAWAY: + case WS_TELTO: + case WS_CANCEL: + y = hero.y; + x = hero.x; + while (step_ok(winat(y, x))) + { + y += delta.y; + x += delta.x; + } + if ((tp = moat(y, x)) != NULL) + { + monster = tp->t_type; + if (monster == 'F') + player.t_flags &= ~ISHELD; + switch (obj->o_which) { + case WS_INVIS: + tp->t_flags |= ISINVIS; + if (cansee(rs,y, x)) + mvaddch(y, x, tp->t_oldch); + break; + case WS_POLYMORPH: + { + THING *pp; + + pp = tp->t_pack; + detach(mlist, tp); + if (see_monst(tp)) + mvaddch(y, x, chat(y, x)); + oldch = tp->t_oldch; + delta.y = y; + delta.x = x; + new_monster(rs,tp, monster = (char)(rnd(26) + 'A'), &delta); + if (see_monst(tp)) + mvaddch(y, x, monster); + tp->t_oldch = oldch; + tp->t_pack = pp; + ws_info[WS_POLYMORPH].oi_know |= see_monst(tp); + break; + } + case WS_CANCEL: + tp->t_flags |= ISCANC; + tp->t_flags &= ~(ISINVIS|CANHUH); + tp->t_disguise = tp->t_type; + if (see_monst(tp)) + mvaddch(y, x, tp->t_disguise); + break; + case WS_TELAWAY: + case WS_TELTO: + { + coord new_pos; + + if (obj->o_which == WS_TELAWAY) + { + do + { + find_floor(rs,NULL, &new_pos, FALSE, TRUE); + } while (ce(new_pos, hero)); + } + else + { + new_pos.y = hero.y + delta.y; + new_pos.x = hero.x + delta.x; + } + tp->t_dest = &hero; + tp->t_flags |= ISRUN; + relocate(rs,tp, &new_pos); + } + } + } + when WS_MISSILE: + ws_info[WS_MISSILE].oi_know = TRUE; + bolt.o_type = '*'; + strncpy(bolt.o_hurldmg,"1x4",sizeof(bolt.o_hurldmg)); + bolt.o_hplus = 100; + bolt.o_dplus = 1; + bolt.o_flags = ISMISL; + if (cur_weapon != NULL) + bolt.o_launch = cur_weapon->o_which; + do_motion(rs,&bolt, delta.y, delta.x); + if ((tp = moat(bolt.o_pos.y, bolt.o_pos.x)) != NULL + && !save_throw(VS_MAGIC, tp)) + hit_monster(rs,unc(bolt.o_pos), &bolt); + else if (terse) + msg(rs,"missle vanishes"); + else + msg(rs,"the missle vanishes with a puff of smoke"); + when WS_HASTE_M: + case WS_SLOW_M: + y = hero.y; + x = hero.x; + while (step_ok(winat(y, x))) + { + y += delta.y; + x += delta.x; + } + if ((tp = moat(y, x)) != NULL) + { + if (obj->o_which == WS_HASTE_M) + { + if (on(*tp, ISSLOW)) + tp->t_flags &= ~ISSLOW; + else + tp->t_flags |= ISHASTE; + } + else + { + if (on(*tp, ISHASTE)) + tp->t_flags &= ~ISHASTE; + else + tp->t_flags |= ISSLOW; + tp->t_turn = TRUE; + } + delta.y = y; + delta.x = x; + runto(rs,&delta); + } + when WS_ELECT: + case WS_FIRE: + case WS_COLD: + if (obj->o_which == WS_ELECT) + name = "bolt"; + else if (obj->o_which == WS_FIRE) + name = "flame"; + else + name = "ice"; + fire_bolt(rs,&hero, &delta, name); + ws_info[obj->o_which].oi_know = TRUE; + when WS_NOP: + break; +#ifdef MASTER + otherwise: + msg(rs,"what a bizarre schtick!"); +#endif + } + obj->o_charges--; +} + +/* + * drain: + * Do drain hit points from player shtick + */ + +void +drain(struct rogue_state *rs) +{ + THING *mp; + struct room *corp; + THING **dp; + int cnt; + bool inpass; + static THING *drainee[40]; + + /* + * First cnt how many things we need to spread the hit points among + */ + cnt = 0; + if (chat(hero.y, hero.x) == DOOR) + corp = &passages[flat(hero.y, hero.x) & F_PNUM]; + else + corp = NULL; + inpass = (bool)(proom->r_flags & ISGONE); + dp = drainee; + for (mp = mlist; mp != NULL; mp = next(mp)) + if (mp->t_room == proom || mp->t_room == corp || + (inpass && chat(mp->t_pos.y, mp->t_pos.x) == DOOR && + &passages[flat(mp->t_pos.y, mp->t_pos.x) & F_PNUM] == proom)) + *dp++ = mp; + if ((cnt = (int)(dp - drainee)) == 0) + { + msg(rs,"you have a tingling feeling"); + return; + } + *dp = NULL; + pstats.s_hpt /= 2; + cnt = pstats.s_hpt / cnt; + /* + * Now zot all of the monsters + */ + for (dp = drainee; *dp; dp++) + { + mp = *dp; + if ((mp->t_stats.s_hpt -= cnt) <= 0) + killed(rs,mp, see_monst(mp)); + else + runto(rs,&mp->t_pos); + } +} + +/* + * fire_bolt: + * Fire a bolt in a given direction from a specific starting place + */ + +void +fire_bolt(struct rogue_state *rs,coord *start, coord *dir, char *name) +{ + coord *c1, *c2; + THING *tp; + char dirch = 0, ch; + bool hit_hero, used, changed; + static coord pos; + static coord spotpos[BOLT_LENGTH]; + THING bolt; + + bolt.o_type = WEAPON; + bolt.o_which = FLAME; + strncpy(bolt.o_hurldmg,"6x6",sizeof(bolt.o_hurldmg)); + bolt.o_hplus = 100; + bolt.o_dplus = 0; + weap_info[FLAME].oi_name = name; + switch (dir->y + dir->x) + { + case 0: dirch = '/'; + when 1: case -1: dirch = (dir->y == 0 ? '-' : '|'); + when 2: case -2: dirch = '\\'; + } + pos = *start; + hit_hero = (bool)(start != &hero); + used = FALSE; + changed = FALSE; + for (c1 = spotpos; c1 <= &spotpos[BOLT_LENGTH-1] && !used; c1++) + { + pos.y += dir->y; + pos.x += dir->x; + *c1 = pos; + ch = winat(pos.y, pos.x); + switch (ch) + { + case DOOR: + /* + * this code is necessary if the hero is on a door + * and he fires at the wall the door is in, it would + * otherwise loop infinitely + */ + if (ce(hero, pos)) + goto def; + /* FALLTHROUGH */ + case '|': + case '-': + case ' ': + if (!changed) + hit_hero = !hit_hero; + changed = FALSE; + dir->y = -dir->y; + dir->x = -dir->x; + c1--; + msg(rs,"the %s bounces", name); + break; + default: +def: + if (!hit_hero && (tp = moat(pos.y, pos.x)) != NULL) + { + hit_hero = TRUE; + changed = !changed; + tp->t_oldch = chat(pos.y, pos.x); + if (!save_throw(VS_MAGIC, tp)) + { + bolt.o_pos = pos; + used = TRUE; + if (tp->t_type == 'D' && strcmp(name, "flame") == 0) + { + addmsg(rs,"the flame bounces"); + if (!terse) + addmsg(rs," off the dragon"); + endmsg(rs); + } + else + hit_monster(rs,unc(pos), &bolt); + } + else if (ch != 'M' || tp->t_disguise == 'M') + { + if (start == &hero) + runto(rs,&pos); + if (terse) + msg(rs,"%s misses", name); + else + msg(rs,"the %s whizzes past %s", name, set_mname(tp)); + } + } + else if (hit_hero && ce(pos, hero)) + { + hit_hero = FALSE; + changed = !changed; + if (!save(VS_MAGIC)) + { + if ((pstats.s_hpt -= roll(6, 6)) <= 0) + { + if (start == &hero) + death(rs,'b'); + else + death(rs,moat(start->y, start->x)->t_type); + } + used = TRUE; + if (terse) + msg(rs,"the %s hits", name); + else + msg(rs,"you are hit by the %s", name); + } + else + msg(rs,"the %s whizzes by you", name); + } + mvaddch(pos.y, pos.x, dirch); + if ( rs->sleeptime != 0 ) + refresh(); + } + } + for (c2 = spotpos; c2 < c1; c2++) + mvaddch(c2->y, c2->x, chat(c2->y, c2->x)); +} + +/* + * charge_str: + * Return an appropriate string for a wand charge + */ +char * +charge_str(THING *obj) +{ + static char buf[20]; + + if (!(obj->o_flags & ISKNOW)) + buf[0] = '\0'; + else if (terse) + sprintf(buf, " [%d]", obj->o_charges); + else + sprintf(buf, " [%d charges]", obj->o_charges); + return buf; +} diff --git a/src/cc/rogue/things.c b/src/cc/rogue/things.c new file mode 100644 index 000000000..e0cf48454 --- /dev/null +++ b/src/cc/rogue/things.c @@ -0,0 +1,738 @@ +/* + * Contains functions for dealing with things like potions, scrolls, + * and other items. + * + * @(#)things.c 4.53 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +#include "rogue.h" + +/* + * inv_name: + * Return the name of something as it would appear in an + * inventory. + */ +char * +inv_name(THING *obj, bool drop) +{ + char *pb; + struct obj_info *op; + char *sp; + int which; + + pb = prbuf; + which = obj->o_which; + switch (obj->o_type) + { + case POTION: + nameit(obj, "potion", p_colors[which], &pot_info[which], nullstr); + when RING: + nameit(obj, "ring", r_stones[which], &ring_info[which], ring_num); + when STICK: + nameit(obj, ws_type[which], ws_made[which], &ws_info[which], charge_str); + when SCROLL: + if (obj->o_count == 1) + { + strcpy(pb, "A scroll "); + pb = &prbuf[9]; + } + else + { + sprintf(pb, "%d scrolls ", obj->o_count); + pb = &prbuf[strlen(prbuf)]; + } + op = &scr_info[which]; + if (op->oi_know) + sprintf(pb, "of %s", op->oi_name); + else if (op->oi_guess) + sprintf(pb, "called %s", op->oi_guess); + else + sprintf(pb, "titled '%s'", s_names[which]); + when FOOD: + if (which == 1) + if (obj->o_count == 1) + sprintf(pb, "A%s %s", vowelstr(fruit), fruit); + else + sprintf(pb, "%d %ss", obj->o_count, fruit); + else + if (obj->o_count == 1) + strcpy(pb, "Some food"); + else + sprintf(pb, "%d rations of food", obj->o_count); + when WEAPON: + sp = weap_info[which].oi_name; + if (obj->o_count > 1) + sprintf(pb, "%d ", obj->o_count); + else + sprintf(pb, "A%s ", vowelstr(sp)); + pb = &prbuf[strlen(prbuf)]; + if (obj->o_flags & ISKNOW) + sprintf(pb, "%s %s", num(obj->o_hplus,obj->o_dplus,WEAPON), sp); + else + sprintf(pb, "%s", sp); + if (obj->o_count > 1) + strcat(pb, "s"); + if (obj->o_label != NULL) + { + pb = &prbuf[strlen(prbuf)]; + sprintf(pb, " called %s", obj->o_label); + } + when ARMOR: + sp = arm_info[which].oi_name; + if (obj->o_flags & ISKNOW) + { + sprintf(pb, "%s %s [", + num(a_class[which] - obj->o_arm, 0, ARMOR), sp); + if (!terse) + strcat(pb, "protection "); + pb = &prbuf[strlen(prbuf)]; + sprintf(pb, "%d]", 10 - obj->o_arm); + } + else + sprintf(pb, "%s", sp); + if (obj->o_label != NULL) + { + pb = &prbuf[strlen(prbuf)]; + sprintf(pb, " called %s", obj->o_label); + } + when AMULET: + strcpy(pb, "The Amulet of Yendor"); + when GOLD: + sprintf(prbuf, "%d Gold pieces", obj->o_goldval); +#ifdef MASTER + otherwise: + debug("Picked up something funny %s", unctrl(obj->o_type)); + sprintf(pb, "Something bizarre %s", unctrl(obj->o_type)); +#endif + } + if (inv_describe) + { + if (obj == cur_armor) + strcat(pb, " (being worn)"); + if (obj == cur_weapon) + strcat(pb, " (weapon in hand)"); + if (obj == cur_ring[LEFT]) + strcat(pb, " (on left hand)"); + else if (obj == cur_ring[RIGHT]) + strcat(pb, " (on right hand)"); + } + if (drop && isupper(prbuf[0])) + prbuf[0] = (char) tolower(prbuf[0]); + else if (!drop && islower(*prbuf)) + *prbuf = (char) toupper(*prbuf); + prbuf[MAXSTR-1] = '\0'; + return prbuf; +} + +/* + * drop: + * Put something down + */ + +void +drop(struct rogue_state *rs) +{ + char ch; + THING *obj; + + ch = chat(hero.y, hero.x); + if (ch != FLOOR && ch != PASSAGE) + { + after = FALSE; + msg(rs,"there is something there already"); + return; + } + if ((obj = get_item(rs,"drop", 0)) == NULL) + return; + if (!dropcheck(rs,obj)) + return; + obj = leave_pack(rs,obj, TRUE, (bool)!ISMULT(obj->o_type)); + /* + * Link it into the level object list + */ + attach(lvl_obj, obj); + chat(hero.y, hero.x) = (char) obj->o_type; + flat(hero.y, hero.x) |= F_DROPPED; + obj->o_pos = hero; + if (obj->o_type == AMULET) + amulet = FALSE; + msg(rs,"dropped %s", inv_name(obj, TRUE)); +} + +/* + * dropcheck: + * Do special checks for dropping or unweilding|unwearing|unringing + */ +bool +dropcheck(struct rogue_state *rs,THING *obj) +{ + if (obj == NULL) + return TRUE; + if (obj != cur_armor && obj != cur_weapon + && obj != cur_ring[LEFT] && obj != cur_ring[RIGHT]) + return TRUE; + if (obj->o_flags & ISCURSED) + { + msg(rs,"you can't. It appears to be cursed"); + return FALSE; + } + if (obj == cur_weapon) + cur_weapon = NULL; + else if (obj == cur_armor) + { + waste_time(rs); + cur_armor = NULL; + } + else + { + cur_ring[obj == cur_ring[LEFT] ? LEFT : RIGHT] = NULL; + switch (obj->o_which) + { + case R_ADDSTR: + chg_str(-obj->o_arm); + break; + case R_SEEINVIS: + unsee(rs,0); + extinguish(unsee); + break; + } + } + return TRUE; +} + +/* + * new_thing: + * Return a new thing + */ +THING * +new_thing(struct rogue_state *rs) +{ + THING *cur; + int r; + + cur = new_item(); + // 7th new_thing orphaned fprintf(stderr,"new_thing.%p\n",cur); + cur->o_hplus = 0; + cur->o_dplus = 0; + strncpy(cur->o_damage, "0x0", sizeof(cur->o_damage)); + strncpy(cur->o_hurldmg, "0x0", sizeof(cur->o_hurldmg)); + cur->o_arm = 11; + cur->o_count = 1; + cur->o_group = 0; + cur->o_flags = 0; + /* + * Decide what kind of object it will be + * If we haven't had food for a while, let it be food. + */ + switch (no_food > 3 ? 2 : pick_one(rs,things, NUMTHINGS)) + { + case 0: + cur->o_type = POTION; + cur->o_which = pick_one(rs,pot_info, MAXPOTIONS); + when 1: + cur->o_type = SCROLL; + cur->o_which = pick_one(rs,scr_info, MAXSCROLLS); + when 2: + cur->o_type = FOOD; + no_food = 0; + if (rnd(10) != 0) + cur->o_which = 0; + else + cur->o_which = 1; + when 3: + init_weapon(cur, pick_one(rs,weap_info, MAXWEAPONS)); + if ((r = rnd(100)) < 10) + { + cur->o_flags |= ISCURSED; + cur->o_hplus -= rnd(3) + 1; + } + else if (r < 15) + cur->o_hplus += rnd(3) + 1; + when 4: + cur->o_type = ARMOR; + cur->o_which = pick_one(rs,arm_info, MAXARMORS); + cur->o_arm = a_class[cur->o_which]; + if ((r = rnd(100)) < 20) + { + cur->o_flags |= ISCURSED; + cur->o_arm += rnd(3) + 1; + } + else if (r < 28) + cur->o_arm -= rnd(3) + 1; + when 5: + cur->o_type = RING; + cur->o_which = pick_one(rs,ring_info, MAXRINGS); + switch (cur->o_which) + { + case R_ADDSTR: + case R_PROTECT: + case R_ADDHIT: + case R_ADDDAM: + if ((cur->o_arm = rnd(3)) == 0) + { + cur->o_arm = -1; + cur->o_flags |= ISCURSED; + } + when R_AGGR: + case R_TELEPORT: + cur->o_flags |= ISCURSED; + } + when 6: + cur->o_type = STICK; + cur->o_which = pick_one(rs,ws_info, MAXSTICKS); + fix_stick(cur); +#ifdef MASTER + otherwise: + debug("Picked a bad kind of object"); + wait_for(rs,' '); +#endif + } + return cur; +} + +/* + * pick_one: + * Pick an item out of a list of nitems possible objects + */ +int +pick_one(struct rogue_state *rs,struct obj_info *info, int nitems) +{ + struct obj_info *end; + struct obj_info *start; + int i; + + start = info; + for (end = &info[nitems], i = rnd(100); info < end; info++) + if (i < info->oi_prob) + break; + if (info == end) + { +#ifdef MASTER + if (wizard) + { + msg(rs,"bad pick_one: %d from %d items", i, nitems); + for (info = start; info < end; info++) + msg(rs,"%s: %d%%", info->oi_name, info->oi_prob); + } +#endif + info = start; + } + return (int)(info - start); +} + +/* + * discovered: + * list what the player has discovered in this game of a certain type + */ +static int line_cnt = 0; + +static bool newpage = FALSE; + +static char *lastfmt, *lastarg; + + +void +discovered(struct rogue_state *rs) +{ + char ch; + bool disc_list; + + do { + disc_list = FALSE; + if (!terse) + addmsg(rs,"for "); + addmsg(rs,"what type"); + if (!terse) + addmsg(rs," of object do you want a list"); + msg(rs,"? (* for all)"); + ch = readchar(rs); + switch (ch) + { + case ESCAPE: + msg(rs,""); + return; + case POTION: + case SCROLL: + case RING: + case STICK: + case '*': + disc_list = TRUE; + break; + default: + if (terse) + msg(rs,"Not a type"); + else + msg(rs,"Please type one of %c%c%c%c (ESCAPE to quit)", POTION, SCROLL, RING, STICK); + } + } while (!disc_list); + if (ch == '*') + { + print_disc(rs,POTION); + add_line(rs,"", NULL); + print_disc(rs,SCROLL); + add_line(rs,"", NULL); + print_disc(rs,RING); + add_line(rs,"", NULL); + print_disc(rs,STICK); + end_line(rs); + } + else + { + print_disc(rs,ch); + end_line(rs); + } +} + +/* + * print_disc: + * Print what we've discovered of type 'type' + */ + +#define MAX4(a,b,c,d) (a > b ? (a > c ? (a > d ? a : d) : (c > d ? c : d)) : (b > c ? (b > d ? b : d) : (c > d ? c : d))) + + +void +print_disc(struct rogue_state *rs,char type) +{ + struct obj_info *info = NULL; + int i, maxnum = 0, num_found; + static THING obj; + static int order[MAX4(MAXSCROLLS, MAXPOTIONS, MAXRINGS, MAXSTICKS)]; + + switch (type) + { + case SCROLL: + maxnum = MAXSCROLLS; + info = scr_info; + break; + case POTION: + maxnum = MAXPOTIONS; + info = pot_info; + break; + case RING: + maxnum = MAXRINGS; + info = ring_info; + break; + case STICK: + maxnum = MAXSTICKS; + info = ws_info; + break; + } + set_order(order, maxnum); + obj.o_count = 1; + obj.o_flags = 0; + num_found = 0; + for (i = 0; i < maxnum; i++) + if (info[order[i]].oi_know || info[order[i]].oi_guess) + { + obj.o_type = type; + obj.o_which = order[i]; + add_line(rs,"%s", inv_name(&obj, FALSE)); + num_found++; + } + if (num_found == 0) + add_line(rs,nothing(type), NULL); +} + +/* + * set_order: + * Set up order for list + */ + +void +set_order(int *order, int numthings) +{ + int i, r, t; + + for (i = 0; i< numthings; i++) + order[i] = i; + + for (i = numthings; i > 0; i--) + { + r = rnd(i); + t = order[i - 1]; + order[i - 1] = order[r]; + order[r] = t; + } +} + +/* + * add_line: + * Add a line to the list of discoveries + */ +/* VARARGS1 */ +char +add_line(struct rogue_state *rs,char *fmt, char *arg) +{ + WINDOW *tw, *sw; + int x, y; + char *prompt = "--Press space to continue--"; + static int maxlen = -1; + + if (line_cnt == 0) + { + wclear(hw); + if (inv_type == INV_SLOW) + mpos = 0; + } + if (inv_type == INV_SLOW) + { + if (*fmt != '\0') + if (msg(rs,fmt, arg) == ESCAPE) + return ESCAPE; + line_cnt++; + } + else + { + if (maxlen < 0) + maxlen = (int) strlen(prompt); + if (line_cnt >= LINES - 1 || fmt == NULL) + { + if (inv_type == INV_OVER && fmt == NULL && !newpage) + { + msg(rs,""); + if ( rs->sleeptime != 0 ) + refresh(); + if ( rs->guiflag != 0 ) + { + tw = newwin(line_cnt + 1, maxlen + 2, 0, COLS - maxlen - 3); + sw = subwin(tw, line_cnt + 1, maxlen + 1, 0, COLS - maxlen - 2); + for (y = 0; y <= line_cnt; y++) + { + wmove(sw, y, 0); + for (x = 0; x <= maxlen; x++) + waddch(sw, mvwinch(hw, y, x)); + } + wmove(tw, line_cnt, 1); + waddstr(tw, prompt); + /* + * if there are lines below, use 'em + */ + if (LINES > NUMLINES) + { + if (NUMLINES + line_cnt > LINES) + mvwin(tw, LINES - (line_cnt + 1), COLS - maxlen - 3); + else + mvwin(tw, NUMLINES, 0); + } + touchwin(tw); + wrefresh(tw); + wait_for(rs,' '); + + if (md_hasclreol()) + { + werase(tw); + leaveok(tw, TRUE); + wrefresh(tw); + } + delwin(tw); + touchwin(stdscr); + } + else + { + wait_for(rs,' '); + } + } + else + { + char *promptex = "--Wait 5 sec.--"; + wmove(hw, LINES - 1, 0); + waddstr(hw, newpage ? promptex : prompt); + wrefresh(hw); + + if (newpage) { + + #ifdef _WIN32 + #ifdef _MSC_VER + #define sleep(x) Sleep(1000*(x)) + #endif + #endif + sleep(5); + + } else + wait_for(rs, ' '); + + clearok(curscr, TRUE); + wclear(hw); + + touchwin(stdscr); + } + newpage = TRUE; + line_cnt = 0; + + maxlen = (int) strlen(prompt); + } + if (fmt != NULL && !(line_cnt == 0 && *fmt == '\0')) + { + mvwprintw(hw, line_cnt++, 0, fmt, arg); + getyx(hw, y, x); + if (maxlen < x) + maxlen = x; + lastfmt = fmt; + lastarg = arg; + } + } + return ~ESCAPE; +} + +/* + * end_line: + * End the list of lines + */ + +void +end_line(struct rogue_state *rs) +{ + if (inv_type != INV_SLOW) + { + if (line_cnt == 1 && !newpage) + { + mpos = 0; + msg(rs,lastfmt, lastarg); + } + else + add_line(rs,(char *) NULL, NULL); + } + line_cnt = 0; + newpage = FALSE; +} + +/* + * nothing: + * Set up prbuf so that message for "nothing found" is there + */ +char * +nothing(char type) +{ + char *sp, *tystr = NULL; + + if (terse) + sprintf(prbuf, "Nothing"); + else + sprintf(prbuf, "Haven't discovered anything"); + if (type != '*') + { + sp = &prbuf[strlen(prbuf)]; + switch (type) + { + case POTION: tystr = "potion"; + when SCROLL: tystr = "scroll"; + when RING: tystr = "ring"; + when STICK: tystr = "stick"; + } + sprintf(sp, " about any %ss", tystr); + } + return prbuf; +} + +/* + * nameit: + * Give the proper name to a potion, stick, or ring + */ + +void +nameit(THING *obj, const char *type,const char *which, struct obj_info *op, + char *(*prfunc)(THING *)) +{ + char *pb; + + if (op->oi_know || op->oi_guess) + { + if (obj->o_count == 1) + sprintf(prbuf, "A %s ", type); + else + sprintf(prbuf, "%d %ss ", obj->o_count, type); + pb = &prbuf[strlen(prbuf)]; + if (op->oi_know) + sprintf(pb, "of %s%s(%s)", op->oi_name, (*prfunc)(obj), which); + else if (op->oi_guess) + sprintf(pb, "called %s%s(%s)", op->oi_guess, (*prfunc)(obj), which); + } + else if (obj->o_count == 1) + sprintf(prbuf, "A%s %s %s", vowelstr((char *)which), which, type); + else + sprintf(prbuf, "%d %s %ss", obj->o_count, which, type); +} + +/* + * nullstr: + * Return a pointer to a null-length string + */ +char * +nullstr(THING *ignored) +{ + NOOP(ignored); + return ""; +} + +# ifdef MASTER +/* + * pr_list: + * List possible potions, scrolls, etc. for wizard. + */ + +void +pr_list(struct rogue_state *rs) +{ + int ch; + + if (!terse) + addmsg(rs,"for "); + addmsg(rs,"what type"); + if (!terse) + addmsg(rs," of object do you want a list"); + msg(rs,"? "); + ch = readchar(rs); + switch (ch) + { + case POTION: + pr_spec(pot_info, MAXPOTIONS); + when SCROLL: + pr_spec(scr_info, MAXSCROLLS); + when RING: + pr_spec(ring_info, MAXRINGS); + when STICK: + pr_spec(ws_info, MAXSTICKS); + when ARMOR: + pr_spec(arm_info, MAXARMORS); + when WEAPON: + pr_spec(weap_info, MAXWEAPONS); + otherwise: + return; + } +} + +/* + * pr_spec: + * Print specific list of possible items to choose from + */ + +void +pr_spec(struct rogue_state *rs,struct obj_info *info, int nitems) +{ + struct obj_info *endp; + int i, lastprob; + + endp = &info[nitems]; + lastprob = 0; + for (i = '0'; info < endp; i++) + { + if (i == '9' + 1) + i = 'a'; + sprintf(prbuf, "%c: %%s (%d%%%%)", i, info->oi_prob - lastprob); + lastprob = info->oi_prob; + add_line(rs,prbuf, info->oi_name); + info++; + } + end_line(rs); +} +# endif /* MASTER */ diff --git a/src/cc/rogue/vers.c b/src/cc/rogue/vers.c new file mode 100644 index 000000000..6fbf7be87 --- /dev/null +++ b/src/cc/rogue/vers.c @@ -0,0 +1,17 @@ +/* + * Version number. Whenever a new version number is desired, use sccs + * to get vers.c. encstr is declared here to force it to be loaded + * before the version number, and therefore not to be written in saved + * games. + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +char *release = "5.4.4"; +char *encstr = "\300k||`\251Y.'\305\321\201+\277~r\"]\240_\223=1\341)\222\212\241t;\t$\270\314/<#\201\254"; +char *statlist = "\355kl{+\204\255\313idJ\361\214=4:\311\271\341wK<\312\321\213,,7\271/Rk%\b\312\f\246"; +char *version = "rogue (rogueforge) 09/05/07"; diff --git a/src/cc/rogue/weapons.c b/src/cc/rogue/weapons.c new file mode 100644 index 000000000..0a8b6016c --- /dev/null +++ b/src/cc/rogue/weapons.c @@ -0,0 +1,290 @@ +/* + * Functions for dealing with problems brought about by weapons + * + * @(#)weapons.c 4.34 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +#include "rogue.h" + +#define NO_WEAPON -1 + +int group = 2; + +static struct init_weaps { + char *iw_dam; /* Damage when wielded */ + char *iw_hrl; /* Damage when thrown */ + char iw_launch; /* Launching weapon */ + int iw_flags; /* Miscellaneous flags */ +} init_dam[MAXWEAPONS] = { + { "2x4", "1x3", NO_WEAPON, 0, }, /* Mace */ + { "3x4", "1x2", NO_WEAPON, 0, }, /* Long sword */ + { "1x1", "1x1", NO_WEAPON, 0, }, /* Bow */ + { "1x1", "2x3", BOW, ISMANY|ISMISL, }, /* Arrow */ + { "1x6", "1x4", NO_WEAPON, ISMISL|ISMISL, }, /* Dagger */ + { "4x4", "1x2", NO_WEAPON, 0, }, /* 2h sword */ + { "1x1", "1x3", NO_WEAPON, ISMANY|ISMISL, }, /* Dart */ + { "1x2", "2x4", NO_WEAPON, ISMANY|ISMISL, }, /* Shuriken */ + { "2x3", "1x6", NO_WEAPON, ISMISL, }, /* Spear */ +}; + +/* + * missile: + * Fire a missile in a given direction + */ + +void +missile(struct rogue_state *rs,int ydelta, int xdelta) +{ + THING *obj; + + /* + * Get which thing we are hurling + */ + if ((obj = get_item(rs,"throw", WEAPON)) == NULL) + return; + if (!dropcheck(rs,obj) || is_current(rs,obj)) + return; + obj = leave_pack(rs,obj, TRUE, FALSE); + do_motion(rs,obj, ydelta, xdelta); + /* + * AHA! Here it has hit something. If it is a wall or a door, + * or if it misses (combat) the monster, put it on the floor + */ + if (moat(obj->o_pos.y, obj->o_pos.x) == NULL || + !hit_monster(rs,unc(obj->o_pos), obj)) + fall(rs,obj, TRUE); +} + +/* + * do_motion: + * Do the actual motion on the screen done by an object traveling + * across the room + */ + +void +do_motion(struct rogue_state *rs,THING *obj, int ydelta, int xdelta) +{ + int ch; + + /* + * Come fly with us ... + */ + obj->o_pos = hero; + for (;;) + { + if ( rs->replaydone != 0 ) + return; + /* + * Erase the old one + */ + if (!ce(obj->o_pos, hero) && cansee(rs,unc(obj->o_pos)) && !terse) + { + ch = chat(obj->o_pos.y, obj->o_pos.x); + if (ch == FLOOR && !show_floor()) + ch = ' '; + mvaddch(obj->o_pos.y, obj->o_pos.x, ch); + } + /* + * Get the new position + */ + obj->o_pos.y += ydelta; + obj->o_pos.x += xdelta; + if (step_ok(ch = winat(obj->o_pos.y, obj->o_pos.x)) && ch != DOOR) + { + /* + * It hasn't hit anything yet, so display it + * If it alright. + */ + if (cansee(rs,unc(obj->o_pos)) && !terse) + { + mvaddch(obj->o_pos.y, obj->o_pos.x, obj->o_type); + if ( rs->sleeptime != 0 ) + refresh(); + } + continue; + } + break; + } +} + +/* + * fall: + * Drop an item someplace around here. + */ + +void +fall(struct rogue_state *rs,THING *obj, bool pr) +{ + PLACE *pp; + static coord fpos; + + if (fallpos(&obj->o_pos, &fpos)) + { + pp = INDEX(fpos.y, fpos.x); + pp->p_ch = (char) obj->o_type; + obj->o_pos = fpos; + if (cansee(rs,fpos.y, fpos.x)) + { + if (pp->p_monst != NULL) + pp->p_monst->t_oldch = (char) obj->o_type; + else + mvaddch(fpos.y, fpos.x, obj->o_type); + } + attach(lvl_obj, obj); + return; + } + if (pr) + { + if (has_hit) + { + endmsg(rs); + has_hit = FALSE; + } + msg(rs,"the %s vanishes as it hits the ground", + weap_info[obj->o_which].oi_name); + } + discard(obj); +} + +/* + * init_weapon: + * Set up the initial goodies for a weapon + */ + +void +init_weapon(THING *weap, int which) +{ + struct init_weaps *iwp; + weap->o_type = WEAPON; + weap->o_which = which; + iwp = &init_dam[which]; + strncpy(weap->o_damage, iwp->iw_dam, sizeof(weap->o_damage)); + strncpy(weap->o_hurldmg,iwp->iw_hrl, sizeof(weap->o_hurldmg)); + weap->o_launch = iwp->iw_launch; + weap->o_flags = iwp->iw_flags; + weap->o_hplus = 0; + weap->o_dplus = 0; + if (which == DAGGER) + { + weap->o_count = rnd(4) + 2; + weap->o_group = group++; + } + else if (weap->o_flags & ISMANY) + { + weap->o_count = rnd(8) + 8; + weap->o_group = group++; + } + else + { + weap->o_count = 1; + weap->o_group = 0; + } +} + +/* + * hit_monster: + * Does the missile hit the monster? + */ +int +hit_monster(struct rogue_state *rs,int y, int x, THING *obj) +{ + static coord mp; + + mp.y = y; + mp.x = x; + return fight(rs,&mp, obj, TRUE); +} + +/* + * num: + * Figure out the plus number for armor/weapons + */ +char * +num(int n1, int n2, char type) +{ + static char numbuf[10]; + + sprintf(numbuf, n1 < 0 ? "%d" : "+%d", n1); + if (type == WEAPON) + sprintf(&numbuf[strlen(numbuf)], n2 < 0 ? ",%d" : ",+%d", n2); + return numbuf; +} + +/* + * wield: + * Pull out a certain weapon + */ + +void +wield(struct rogue_state *rs) +{ + THING *obj, *oweapon; + char *sp; + + oweapon = cur_weapon; + if (!dropcheck(rs,cur_weapon)) + { + cur_weapon = oweapon; + return; + } + cur_weapon = oweapon; + if ((obj = get_item(rs,"wield", WEAPON)) == NULL) + { +bad: + after = FALSE; + return; + } + + if (obj->o_type == ARMOR) + { + msg(rs,"you can't wield armor"); + goto bad; + } + if (is_current(rs,obj)) + goto bad; + + sp = inv_name(obj, TRUE); + cur_weapon = obj; + if (!terse) + addmsg(rs,"you are now "); + msg(rs,"wielding %s (%c)", sp, obj->o_packch); +} + +/* + * fallpos: + * Pick a random position around the give (y, x) coordinates + */ +bool +fallpos(coord *pos, coord *newpos) +{ + int y, x, cnt, ch; + + cnt = 0; + for (y = pos->y - 1; y <= pos->y + 1; y++) + for (x = pos->x - 1; x <= pos->x + 1; x++) + { + /* + * check to make certain the spot is empty, if it is, + * put the object there, set it in the level list + * and re-draw the room if he can see it + */ + if (y == hero.y && x == hero.x) + continue; + if (((ch = chat(y, x)) == FLOOR || ch == PASSAGE) + && rnd(++cnt) == 0) + { + newpos->y = y; + newpos->x = x; + } + } + return (bool)(cnt != 0); +} diff --git a/src/cc/rogue/wizard.c b/src/cc/rogue/wizard.c new file mode 100644 index 000000000..35ce9fb54 --- /dev/null +++ b/src/cc/rogue/wizard.c @@ -0,0 +1,286 @@ +/* + * Special wizard commands (some of which are also non-wizard commands + * under strange circumstances) + * + * @(#)wizard.c 4.30 (Berkeley) 02/05/99 + * + * Rogue: Exploring the Dungeons of Doom + * Copyright (C) 1980-1983, 1985, 1999 Michael Toy, Ken Arnold and Glenn Wichman + * All rights reserved. + * + * See the file LICENSE.TXT for full copyright and licensing information. + */ + +//#include +//#include +//#include +//#include +#include "rogue.h" + +/* + * whatis: + * What a certin object is + */ + +void +whatis(struct rogue_state *rs,bool insist, int type) +{ + THING *obj; + + if (pack == NULL) + { + msg(rs,"you don't have anything in your pack to identify"); + return; + } + + for (;;) + { + if ( rs->replaydone != 0 ) + return; + obj = get_item(rs,"identify", type); + if (insist) + { + if (n_objs == 0) + return; + else if (obj == NULL) + msg(rs,"you must identify something"); + else if (type && obj->o_type != type && + !(type == R_OR_S && (obj->o_type == RING || obj->o_type == STICK)) ) + msg(rs,"you must identify a %s", type_name(type)); + else + break; + } + else + break; + } + + if (obj == NULL) + return; + + switch (obj->o_type) + { + case SCROLL: + set_know(obj, scr_info); + when POTION: + set_know(obj, pot_info); + when STICK: + set_know(obj, ws_info); + when WEAPON: + case ARMOR: + obj->o_flags |= ISKNOW; + when RING: + set_know(obj, ring_info); + } + msg(rs,inv_name(obj, FALSE)); +} + +/* + * set_know: + * Set things up when we really know what a thing is + */ + +void +set_know(THING *obj, struct obj_info *info) +{ + char **guess; + + info[obj->o_which].oi_know = TRUE; + obj->o_flags |= ISKNOW; + guess = &info[obj->o_which].oi_guess; + if (*guess) + { + free(*guess); + *guess = NULL; + } +} + +/* + * type_name: + * Return a pointer to the name of the type + */ +char * +type_name(int type) +{ + struct h_list *hp; + static struct h_list tlist[] = { + {POTION, "potion", FALSE}, + {SCROLL, "scroll", FALSE}, + {FOOD, "food", FALSE}, + {R_OR_S, "ring, wand or staff", FALSE}, + {RING, "ring", FALSE}, + {STICK, "wand or staff", FALSE}, + {WEAPON, "weapon", FALSE}, + {ARMOR, "suit of armor", FALSE}, + }; + + for (hp = tlist; hp->h_ch; hp++) + if (type == hp->h_ch) + return hp->h_desc; + /* NOTREACHED */ + return(0); +} + +#ifdef MASTER +/* + * create_obj: + * wizard command for getting anything he wants + */ + +void +create_obj(struct rogue_state *rs) +{ + THING *obj; + char ch, bless; + + obj = new_item(); + msg(rs,"type of item: "); + obj->o_type = readchar(rs); + mpos = 0; + msg(rs,"which %c do you want? (0-f)", obj->o_type); + obj->o_which = (isdigit((ch = readchar(rs))) ? ch - '0' : ch - 'a' + 10); + obj->o_group = 0; + obj->o_count = 1; + mpos = 0; + if (obj->o_type == WEAPON || obj->o_type == ARMOR) + { + msg(rs,"blessing? (+,-,n)"); + bless = readchar(rs); + mpos = 0; + if (bless == '-') + obj->o_flags |= ISCURSED; + if (obj->o_type == WEAPON) + { + init_weapon(obj, obj->o_which); + if (bless == '-') + obj->o_hplus -= rnd(3)+1; + if (bless == '+') + obj->o_hplus += rnd(3)+1; + } + else + { + obj->o_arm = a_class[obj->o_which]; + if (bless == '-') + obj->o_arm += rnd(3)+1; + if (bless == '+') + obj->o_arm -= rnd(3)+1; + } + } + else if (obj->o_type == RING) + switch (obj->o_which) + { + case R_PROTECT: + case R_ADDSTR: + case R_ADDHIT: + case R_ADDDAM: + msg(rs,"blessing? (+,-,n)"); + bless = readchar(rs); + mpos = 0; + if (bless == '-') + obj->o_flags |= ISCURSED; + obj->o_arm = (bless == '-' ? -1 : rnd(2) + 1); + when R_AGGR: + case R_TELEPORT: + obj->o_flags |= ISCURSED; + } + else if (obj->o_type == STICK) + fix_stick(obj); + else if (obj->o_type == GOLD) + { + msg(rs,"how much?"); + get_num(&obj->o_goldval, stdscr); + } + add_pack(rs,obj, FALSE); +} +#endif + +/* + * telport: + * Bamf the hero someplace else + */ + +void +teleport(struct rogue_state *rs) +{ + static coord c; + + mvaddch(hero.y, hero.x, floor_at()); + find_floor(rs,(struct room *) NULL, &c, FALSE, TRUE); + if (roomin(rs,&c) != proom) + { + leave_room(rs,&hero); + hero = c; + enter_room(rs,&hero); + } + else + { + hero = c; + look(rs,TRUE); + } + mvaddch(hero.y, hero.x, PLAYER); + /* + * turn off ISHELD in case teleportation was done while fighting + * a Flytrap + */ + if (on(player, ISHELD)) { + player.t_flags &= ~ISHELD; + vf_hit = 0; + strcpy(monsters['F'-'A'].m_stats.s_dmg, "000x0"); + } + no_move = 0; + count = 0; + running = FALSE; + flush_type(); +} + +#ifdef MASTER +/* + * passwd: + * See if user knows password + */ +int +passwd(struct rogue_state *rs) +{ + char *sp, c; + static char buf[MAXSTR]; + + msg(rs,"wizard's Password:"); + mpos = 0; + sp = buf; + while ((c = readchar(rs)) != '\n' && c != '\r' && c != ESCAPE) + if (c == md_killchar()) + sp = buf; + else if (c == md_erasechar() && sp > buf) + sp--; + else + *sp++ = c; + if (sp == buf) + return FALSE; + *sp = '\0'; + return (strcmp(PASSWD, md_crypt(buf, "mT")) == 0); +} + +/* + * show_map: + * Print out the map for the wizard + */ + +void +show_map(struct rogue_state *rs) +{ + int y, x, real; + + wclear(hw); + for (y = 1; y < NUMLINES - 1; y++) + for (x = 0; x < NUMCOLS; x++) + { + real = flat(y, x); + if (!(real & F_REAL)) + wstandout(hw); + wmove(hw, y, x); + waddch(hw, chat(y, x)); + if (!real) + wstandend(hw); + } + show_win(rs,"---More (level map)---"); +} +#endif diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/acs_defs.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/acs_defs.h new file mode 100644 index 000000000..c8c02a737 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/acs_defs.h @@ -0,0 +1,265 @@ +/* Many of the following #defines are completely unused for the +nonce. For each character, its code point in code page 437, +Unicode, and page 8859-1 are given. The first is used for +non-wide builds in Win32 console, DOS, SDL, and OS/2. +Unicode is used for all wide builds, and for the non-wide +build of WinGUI. Code page 8859-1 is used for non-wide X11. + + All of these characters exist in CP437 and Unicode. Some +don't exist in 8859-1, in which case the last column is 'TBD'. +Only 32 are used in ncurses. So caution is advised. */ + +#ifdef USE_ISO8859_CHARSET + #define CHOOSE( A, B, C) (C) + #define TBD '!' +#else + #define CHOOSE( A, B, C) (USE_UNICODE_ACS_CHARS ? B : A) +#endif + +/* Codes found from https://en.wikipedia.org/wiki/Code_page_437 */ + +#define SMILE CHOOSE( 0x01, 0x263a, 'O') +#define REV_SMILE CHOOSE( 0x02, 0x263b, 'O') +#define HEART CHOOSE( 0x03, 0x2665, 'H') +#define DIAMOND CHOOSE( 0x04, 0x2666, 0x01) +#define CLUB CHOOSE( 0x05, 0x2663, 'C') +#define SPADE CHOOSE( 0x06, 0x2660, 'S') +#define MEDIUM_BULLET CHOOSE( 0x07, 0x2022, 0xb7) +#define REV_BULLET CHOOSE( 0x08, 0x2508, 0xb7) +#define WHITE_BULLET CHOOSE( 0x09, 0x25cb, 7) +#define REV_WHITE_BULLET CHOOSE( 0x0a, 0x25D9, 7) +#define MALE_SYM CHOOSE( 0x0b, 0x2642, 'm') +#define FEMALE_SYM CHOOSE( 0x0c, 0x2640, 'f') +#define QTR_NOTE CHOOSE( 0x0d, 0x266a, 0xbc) +#define EIGHTH_NOTE CHOOSE( 0x0e, 0x266b, 0xbd) +#define SPLAT CHOOSE( 0x0f, 0xa4 , 0xa4) +#define RIGHT_TRIANGLE CHOOSE( 0x10, 0x25b6, '>') +#define LEFT_TRIANGLE CHOOSE( 0x11, 0x25c0, '<') +#define UP_DOWN_ARROW CHOOSE( 0x12, 0x2195, 0x19) +#define DBL_BANG CHOOSE( 0x13, 0x203c, '!') +#define PILCROW CHOOSE( 0x14, 0xb6 , 0xb6) +#define SECTION_SIGN CHOOSE( 0x15, 0xa7 , 0xa7) +#define LOW_QTR_BLOCK CHOOSE( 0x16, 0x25b2, '_') +#define UP_DOWN_ARROW_UNDERSCORED CHOOSE( 0x17, 0x21ab, 0x19) +#define UP_ARROW CHOOSE( 0x18, 0x2191, '^') +#define DOWN_ARROW CHOOSE( 0x19, 0x2193, 'v') +#define RIGHT_ARROW CHOOSE( 0x1a, 0x2192, '>') +#define LEFT_ARROW CHOOSE( 0x1b, 0x2190, '<') +#define RIGHT_ANGLE CHOOSE( 0x1c, 0x221f, 0xe) +#define LEFT_RIGHT_ARROW CHOOSE( 0x1d, 0x2194, '-') +#define UP_TRIANGLE CHOOSE( 0x1e, 0x25b2, '^') +#define DOWN_TRIANGLE CHOOSE( 0x1f, 0x25bc, 'v') + +#define UPPERCASE_C_CEDILLA CHOOSE( 0x80, 0xc7 , 0xc7) +#define LOWERCASE_U_UMLAUT CHOOSE( 0x81, 0xfc , 0xfc) +#define LOWERCASE_E_ACUTE CHOOSE( 0x82, 0xe9 , 0xe9) +#define LOWERCASE_A_CIRCUMFLEX CHOOSE( 0x83, 0xe2 , 0xe2) +#define LOWERCASE_A_UMLAUT CHOOSE( 0x84, 0xe4 , 0xe4) +#define LOWERCASE_A_GRAVE CHOOSE( 0x85, 0xe0 , 0xea) +#define LOWERCASE_A_RING CHOOSE( 0x86, 0xe5 , 0xe5) +#define LOWERCASE_C_CEDILLA CHOOSE( 0x87, 0xe7 , 0xe7) +#define LOWERCASE_E_CIRCUMFLEX CHOOSE( 0x88, 0xea , 0xea) +#define LOWERCASE_E_UMLAUT CHOOSE( 0x89, 0xeb , 0xeb) +#define LOWERCASE_E_GRAVE CHOOSE( 0x8a, 0xe8 , 0xe8) +#define LOWERCASE_I_UMLAUT CHOOSE( 0x8b, 0xef , 0xef) +#define LOWERCASE_I_CIRCUMFLEX CHOOSE( 0x8c, 0xee , 0xee) +#define LOWERCASE_I_GRAVE CHOOSE( 0x8d, 0xec , 0xce) +#define UPPERCASE_A_UMLAUT CHOOSE( 0x8e, 0xc4 , 0xc4) +#define UPPERCASE_A_RING CHOOSE( 0x8f, 0xc5 , 0xc5) + +#define UPPERCASE_E_ACUTE CHOOSE( 0x90, 0xc9 , 0xc9) +#define LOWERCASE_AE_LIGATURE CHOOSE( 0x91, 0xe6 , 0xe6) +#define UPPERCASE_AE_LIGATURE CHOOSE( 0x92, 0xc6 , 0xc6) +#define LOWERCASE_O_CIRCUMFLEX CHOOSE( 0x93, 0xf4 , 0xf4) +#define LOWERCASE_O_UMLAUT CHOOSE( 0x94, 0xf6 , 0xf6) +#define LOWERCASE_O_GRAVE CHOOSE( 0x95, 0xf2 , 0xf2) +#define LOWERCASE_U_CIRCUMFLEX CHOOSE( 0x96, 0xfb , 0xfb) +#define LOWERCASE_U_GRAVE CHOOSE( 0x97, 0xf9 , 0xf9) +#define LOWERCASE_Y_UMLAUT CHOOSE( 0x98, 0xff , 0xff) +#define UPPERCASE_O_UMLAUT CHOOSE( 0x99, 0xd6 , 0xd6) +#define UPPERCASE_U_UMLAUT CHOOSE( 0x9a, 0xdc , 0xdc) +#define CENT_SIGN CHOOSE( 0x9b, 0xa2 , 0xa2) +#define STERLING_SIGN CHOOSE( 0x9c, 0xa3 , 30) +#define YEN_SIGN CHOOSE( 0x9d, 0xa5 , 0xa5) +#define PESETA_SIGN CHOOSE( 0x9e, 0x20a7, TBD) +#define F_WITH_HOOK CHOOSE( 0x9f, 0x0192, TBD) + +#define LOWERCASE_A_ACUTE CHOOSE( 0xa0, 0xe1 , 0xe1) +#define LOWERCASE_I_ACUTE CHOOSE( 0xa1, 0xed , 0xed) +#define LOWERCASE_O_ACUTE CHOOSE( 0xa2, 0xf3 , 0xf3) +#define LOWERCASE_U_ACUTE CHOOSE( 0xa3, 0xfa , 0xfa) +#define LOWERCASE_N_TILDE CHOOSE( 0xa4, 0xf1 , 0xf1) +#define UPPERCASE_N_TILDE CHOOSE( 0xa5, 0xd1 , 0xd1) +#define A_ORDINAL CHOOSE( 0xa6, 0xaa , 0xaa) +#define O_ORDINAL CHOOSE( 0xa7, 0xba , 0xba) +#define INVERTED_QUESTION_MARK CHOOSE( 0xa8, 0xbf , 0xbf) +#define REVERSED_NOT_SIGN CHOOSE( 0xa9, 0x2310, TBD) +#define NOT_SIGN CHOOSE( 0xaa, 0xac , 0xac) +#define VULGAR_HALF CHOOSE( 0xab, 0xbd , 0xbd) +#define VULGAR_QUARTER CHOOSE( 0xac, 0xbc , 0xbc) +#define INVERTED_EXCLAMATION_MARK CHOOSE( 0xad, 0xa1 , 0xa1) +#define LEFT_ANGLE_QUOTE_MARK CHOOSE( 0xae, 0xab , 0xab) +#define RIGHT_ANGLE_QUOTE_MARK CHOOSE( 0xaf, 0xbb , 0xbb) + +#define LIGHT_SHADE CHOOSE( 0xb0, 0x2591, '#' ) +#define MEDIUM_SHADE CHOOSE( 0xb1, 0x2592, 2) +#define DARK_SHADE CHOOSE( 0xb2, 0x2593, TBD) +#define BOX_VLINE CHOOSE( 0xb3, 0x2502, 25) +#define BOX_RTEE CHOOSE( 0xb4, 0x2524, 22) +#define BOX_SD_RTEE CHOOSE( 0xb5, 0x2561, 22) +#define BOX_DS_RTEE CHOOSE( 0xb6, 0x2562, 22) +#define BOX_DS_URCORNER CHOOSE( 0xb7, 0x2556, 12) +#define BOX_SD_URCORNER CHOOSE( 0xb8, 0x2555, 12) +#define BOX_D_RTEE CHOOSE( 0xb9, 0x2563, 22) +#define BOX_D_VLINE CHOOSE( 0xba, 0x2551, 25) +#define BOX_D_URCORNER CHOOSE( 0xbb, 0x2557, 12) +#define BOX_D_LRCORNER CHOOSE( 0xbc, 0x255D, 11) +#define BOX_DS_LRCORNER CHOOSE( 0xbd, 0x255c, 11) +#define BOX_SD_LRCORNER CHOOSE( 0xbe, 0x255b, 11) +#define BOX_URCORNER CHOOSE( 0xbf, 0x2510, 12) + +#define BOX_LLCORNER CHOOSE( 0xc0, 0x2514, 14) +#define BOX_BTEE CHOOSE( 0xc1, 0x2534, 23) +#define BOX_TTEE CHOOSE( 0xc2, 0x252c, 24) +#define BOX_LTEE CHOOSE( 0xc3, 0x251c, 21) +#define BOX_HLINE CHOOSE( 0xc4, 0x2500, 18) +#define BOX_PLUS CHOOSE( 0xc5, 0x253c, 15) +#define BOX_SD_LTEE CHOOSE( 0xc6, 0x255e, 21) +#define BOX_DS_LTEE CHOOSE( 0xc7, 0x255f, 21) +#define BOX_D_LLCORNER CHOOSE( 0xc8, 0x255A, 14) +#define BOX_D_ULCORNER CHOOSE( 0xc9, 0x2554, 13) +#define BOX_D_BTEE CHOOSE( 0xca, 0x2569, 23) +#define BOX_D_TTEE CHOOSE( 0xcb, 0x2566, 24) +#define BOX_D_LTEE CHOOSE( 0xcc, 0x2560, 21) +#define BOX_D_HLINE CHOOSE( 0xcd, 0x2550, 18) +#define BOX_D_PLUS CHOOSE( 0xce, 0x256C, 15) +#define BOX_SD_BTEE CHOOSE( 0xcf, 0x2567, 23) + +#define BOX_DS_BTEE CHOOSE( 0xd0, 0x2568, 23) +#define BOX_SD_TTEE CHOOSE( 0xd1, 0x2564, 24) +#define BOX_DS_TTEE CHOOSE( 0xd2, 0x2565, 24) +#define BOX_DS_LLCORNER CHOOSE( 0xd3, 0x2559, 14) +#define BOX_SD_LLCORNER CHOOSE( 0xd4, 0x2558, 14) +#define BOX_SD_ULCORNER CHOOSE( 0xd5, 0x2552, 13) +#define BOX_DS_ULCORNER CHOOSE( 0xd6, 0x2553, 13) +#define BOX_DS_PLUS CHOOSE( 0xd7, 0x256b, 15) +#define BOX_SD_PLUS CHOOSE( 0xd8, 0x256a, 15) +#define BOX_LRCORNER CHOOSE( 0xd9, 0x2518, 11) +#define BOX_ULCORNER CHOOSE( 0xda, 0x250c, 13) +#define FULL_BLOCK CHOOSE( 0xdb, 0x2588, 0) +#define LOWER_HALF_BLOCK CHOOSE( 0xdc, 0x2584, TBD) +#define LEFT_HALF_BLOCK CHOOSE( 0xdd, 0x258c, TBD) +#define RIGHT_HALF_BLOCK CHOOSE( 0xde, 0x2590, TBD) +#define UPPER_HALF_BLOCK CHOOSE( 0xdf, 0x2580, TBD) + +#define ALPHA CHOOSE( 0xe0, 0x03b1, TBD) +#define BETA CHOOSE( 0xe1, 0x00df, TBD) +#define GAMMA CHOOSE( 0xe2, 0x0393, TBD) +#define PI CHOOSE( 0xe3, 0x03c0, 28) +#define UPPERCASE_SIGMA CHOOSE( 0xe4, 0x03a3, TBD) +#define LOWERCASE_SIGMA CHOOSE( 0xe5, 0x03c3, TBD) +#define MU CHOOSE( 0xe6, 0x00b5, 0xb5) +#define TAU CHOOSE( 0xe7, 0x03c4, TBD) +#define UPPERCASE_PHI CHOOSE( 0xe8, 0x03a6, TBD) +#define THETA CHOOSE( 0xe9, 0x0398, TBD) +#define OMEGA CHOOSE( 0xea, 0x03a9, TBD) +#define DELTA CHOOSE( 0xeb, 0x03b4, TBD) +#define INFINITY_SIGN CHOOSE( 0xec, 0x221e, TBD) +#define LOWERCASE_PHI CHOOSE( 0xed, 0x03c6, TBD) +#define EPSILON CHOOSE( 0xee, 0x03b5, TBD) +#define INTERSECTION CHOOSE( 0xef, 0x2229, TBD) + +#define TRIPLE_BAR CHOOSE( 0xf0, 0x2261, TBD) +#define PLUS_OR_MINUS CHOOSE( 0xf1, 0x00b1, 8) +#define GREATER_THAN_OR_EQUAL_TO CHOOSE( 0xf2, 0x2265, 27) +#define LESSER_THAN_OR_EQUAL_TO CHOOSE( 0xf3, 0x2264, 26) +#define UPPER_HALF_INTEGRAL_SIGN CHOOSE( 0xf4, 0x2320, TBD) +#define LOWER_HALF_INTEGRAL_SIGN CHOOSE( 0xf5, 0x2321, TBD) +#define DIVISION_SIGN CHOOSE( 0xf6, 0x00f7, 0xf7) +#define APPROXIMATELY_EQUALS_SIGN CHOOSE( 0xf7, 0x2248, TBD) +#define DEGREE_SIGN CHOOSE( 0xf8, 0x00b0, 0xb0) +#define LARGE_BULLET CHOOSE( 0xf9, 0x2219, 7) +#define SMALL_BULLET CHOOSE( 0xfa, 0x00b7, 0xb7) +#define SQUARE_ROOT CHOOSE( 0xfb, 0x221a, TBD) +#define SUPERSCRIPT_N CHOOSE( 0xfc, 0x207f, TBD) +#define SUPERSCRIPT_2 CHOOSE( 0xfd, 0x00b2, 0xb2) +#define CENTERED_SQUARE CHOOSE( 0xfe, 0x25a0, TBD) +#define NON_BREAKING_SPACE CHOOSE( 0xff, 0x00a0, TBD) + + + + /* It says at http://unicode.org/charts/PDF/U2300.pdf */ + /* that '...the scan line numbers here refer to old, */ + /* low-resolution technology for terminals, with only */ + /* nine scan lines per fixed-size character glyph. */ + /* Even-numbered scan lines are unified with box */ + /* drawing graphics." */ + /* The utility of these is questionable; they'd */ + /* work Just Fine in wingdi (_if_ the appropriate */ + /* glyphs are available), but not elsewhere. */ +#define HORIZ_SCAN_LINE_1 CHOOSE( 0x2d, 0x23ba, 16) +#define HORIZ_SCAN_LINE_3 CHOOSE( 0x2d, 0x23bb, 17) +#define HORIZ_SCAN_LINE_7 CHOOSE( 0x2d, 0x23bc, 19) +#define HORIZ_SCAN_LINE_9 CHOOSE( '_', 0x23bd, 20) + + /* Code page 437 lacks a 'for real' not-equals, so for that, */ + /* we use the double-horizontal single-vertical box drawing : */ +#define NOT_EQUALS_SIGN CHOOSE( 0xd8, 0x2260, 29) + +# define A(x) ((chtype)x | A_ALTCHARSET) + +chtype acs_map[128] = +{ + A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), + A(9), A(10), + CLUB, HEART, SPADE, SMILE, REV_SMILE, /* 11 12 13 14 15 */ + MEDIUM_BULLET, WHITE_BULLET, PILCROW, SECTION_SIGN, /* 16 17 18 19 */ + A_ORDINAL, O_ORDINAL, LOWERCASE_PHI, /* 20 21 22 */ + INVERTED_EXCLAMATION_MARK, INVERTED_QUESTION_MARK, /* 23 24 */ + REVERSED_NOT_SIGN, NOT_SIGN, /* 25 26 */ + UPPER_HALF_INTEGRAL_SIGN, LOWER_HALF_INTEGRAL_SIGN, /* 27 28 */ + SUPERSCRIPT_N, CENTERED_SQUARE, F_WITH_HOOK, /* 29 30 31 */ + + RIGHT_ARROW, LEFT_ARROW, UP_ARROW, DOWN_ARROW, /* 32 !"# */ + + PI, NOT_EQUALS_SIGN, VULGAR_HALF, VULGAR_QUARTER, /* $%&' */ + '(', + LEFT_ANGLE_QUOTE_MARK, RIGHT_ANGLE_QUOTE_MARK, /* )* */ + DARK_SHADE, SUPERSCRIPT_2, INFINITY_SIGN, /* +,- */ + ALPHA, BETA, GAMMA, UPPERCASE_SIGMA, LOWERCASE_SIGMA, /* ./012 */ + '3', + MU, TAU, UPPERCASE_PHI, THETA, OMEGA, DELTA, EPSILON, /* 456789: */ + + BOX_SD_LRCORNER, BOX_SD_URCORNER, BOX_SD_ULCORNER, /* ;<= */ + BOX_SD_LLCORNER, BOX_SD_PLUS, /* >? */ + BOX_SD_LTEE, BOX_SD_RTEE, BOX_SD_BTEE, BOX_SD_TTEE, /* @ABC */ + + BOX_D_LRCORNER, BOX_D_URCORNER, BOX_D_ULCORNER, /* DEF */ + BOX_D_LLCORNER, BOX_D_PLUS, /* GH */ + BOX_D_LTEE, BOX_D_RTEE, BOX_D_BTEE, BOX_D_TTEE, /* IJKL */ + + BOX_DS_LRCORNER, BOX_DS_URCORNER, BOX_DS_ULCORNER, /* MNO */ + BOX_DS_LLCORNER, BOX_DS_PLUS, /* PQ */ + BOX_DS_LTEE, BOX_DS_RTEE, BOX_DS_BTEE, BOX_DS_TTEE, /* RSTU */ + + BOX_LRCORNER, BOX_URCORNER, BOX_ULCORNER, /* VWX */ + BOX_LLCORNER, BOX_PLUS, /* YZ */ + BOX_LTEE, BOX_RTEE, BOX_BTEE, BOX_TTEE, /* [\]^ */ + + BOX_HLINE, BOX_VLINE, BOX_D_HLINE, BOX_D_VLINE, /* _`ab */ + + DIVISION_SIGN, APPROXIMATELY_EQUALS_SIGN, /* cd */ + INTERSECTION, TRIPLE_BAR, /* ef */ + SMALL_BULLET, LARGE_BULLET, SQUARE_ROOT, /* ghi */ + DIAMOND, MEDIUM_SHADE, /* jk */ + HORIZ_SCAN_LINE_1, HORIZ_SCAN_LINE_3, /* lm */ + HORIZ_SCAN_LINE_7, HORIZ_SCAN_LINE_9, /* no */ + UPPER_HALF_BLOCK, LOWER_HALF_BLOCK, /* pq */ + LEFT_HALF_BLOCK, RIGHT_HALF_BLOCK, FULL_BLOCK, /* rst */ + LESSER_THAN_OR_EQUAL_TO, GREATER_THAN_OR_EQUAL_TO, /* uv */ + DEGREE_SIGN, PLUS_OR_MINUS, LIGHT_SHADE, SPLAT, /* wxyz */ + CENT_SIGN, YEN_SIGN, PESETA_SIGN, STERLING_SIGN, /* {|}~ */ + A(127) +}; + +# undef A diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curses.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curses.h new file mode 100644 index 000000000..9ee3f08a6 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curses.h @@ -0,0 +1,1846 @@ +/* Public Domain Curses */ + +/*----------------------------------------------------------------------* + * PDCurses * + *----------------------------------------------------------------------*/ + +#ifndef __PDCURSES__ +#define __PDCURSES__ 1 + +/*man-start************************************************************** + +PDCurses definitions list: (Only define those needed) + + XCURSES True if compiling for X11. + PDC_RGB True if you want to use RGB color definitions + (Red = 1, Green = 2, Blue = 4) instead of BGR. + PDC_WIDE True if building wide-character support. + PDC_DLL_BUILD True if building a Windows DLL. + PDC_NCMOUSE Use the ncurses mouse API instead + of PDCurses' traditional mouse API. + +PDCurses portable platform definitions list: + + PDC_BUILD Defines API build version. + PDCURSES Enables access to PDCurses-only routines. + XOPEN Always true. + SYSVcurses True if you are compiling for SYSV portability. + BSDcurses True if you are compiling for BSD portability. + +**man-end****************************************************************/ + +#define PDCURSES 1 /* PDCurses-only routines */ +#define XOPEN 1 /* X/Open Curses routines */ +#define SYSVcurses 1 /* System V Curses routines */ +#define BSDcurses 1 /* BSD Curses routines */ +#if defined( CHTYPE_32) + #define CHTYPE_LONG 1 /* chtypes will be 32 bits */ +#elif !defined( CHTYPE_16) + #define CHTYPE_LONG 2 /* chtypes will be (default) 64 bits */ +#endif + +/*----------------------------------------------------------------------*/ + +#ifdef NO_STDINT_H + #define uint64_t unsigned long long + #define uint32_t unsigned long + #define uint16_t unsigned short +#else + #include +#endif +#include +#include +#include /* Required by X/Open usage below */ + +#ifdef PDC_WIDE +# include +#endif + +#if defined(__STDC_VERSION__) && __STDC_VERSION >= 199901L && \ + !defined(__bool_true_false_are_defined) +# include +#endif + +#ifdef __cplusplus +extern "C" +{ +# define bool _bool +#endif + +/*---------------------------------------------------------------------- + * + * Constants and Types + * + */ + +#undef FALSE +#undef TRUE + +#ifdef __bool_true_false_are_defined + +# define FALSE false +# define TRUE true + +#else + +typedef unsigned char bool; + +# define FALSE 0 +# define TRUE 1 + +#endif + +#undef ERR +#define ERR (-1) + +#undef OK +#define OK 0 + +#ifdef CHTYPE_LONG + #if(CHTYPE_LONG >= 2) /* "non-standard" 64-bit chtypes */ + typedef uint64_t chtype; + #else /* "Standard" CHTYPE_LONG case, 32-bit: */ + typedef uint32_t chtype; + # endif +#else +typedef uint16_t chtype; /* 8-bit attr + 8-bit char */ +#endif + +#ifdef PDC_WIDE +typedef chtype cchar_t; +#endif + +typedef chtype attr_t; + +/* Version constants, available as of version 4.0 : */ +/* Don't forget to update 'version.mif' if MAJOR/MINOR changes! */ + +#define PDC_VER_MAJOR 4 +#define PDC_VER_MINOR 0 +#define PDC_VER_CHANGE 4 +#define PDC_VER_YEAR 2019 +#define PDC_VER_MONTH 1 +#define PDC_VER_DAY 20 + +#define PDC_BUILD (PDC_VER_MAJOR*1000 + PDC_VER_MINOR *100 + PDC_VER_CHANGE) + +/* When using PDCurses as a DLL (Windows) or shared library (BSD or *nix), +it's possible to switch the DLL or shared library. One may therefore want +to inquire of the DLL/shared library the port, version numbers, and +chtype_size used, and make sure they're what one was expecting. The +'PDC_version' structure lets you do just that. */ + +enum PDC_port +{ + PDC_PORT_X11 = 0, + PDC_PORT_WIN32 = 1, + PDC_PORT_WINGUI = 2, + PDC_PORT_DOS = 3, + PDC_PORT_OS2 = 4, + PDC_PORT_SDL1 = 5, + PDC_PORT_SDL2 = 6, + PDC_PORT_VT = 7 +}; + +/* Detailed PDC version information */ +#define PDC_HAS_VERSION_INFO 1 +typedef struct +{ + const enum PDC_port port; + const int ver_major; + const int ver_minor; + const int ver_change; + const size_t chtype_size; + const bool is_wide; + const bool is_forced_utf8; +} PDC_version_info; + +/*---------------------------------------------------------------------- + * + * Mouse Interface -- SYSVR4, with extensions + * + */ + +/* Most flavors of PDCurses support three buttons. WinGUI supports */ +/* these plus two "extended" buttons. But we'll set this macro to */ +/* six, allowing future versions to support up to nine total buttons. */ +/* (The button states are broken up into two arrays to allow for the */ +/* possibility of backward compatibility to DLLs compiled with only */ +/* three mouse buttons.) */ + +#define PDC_MAX_MOUSE_BUTTONS 9 +#define PDC_N_EXTENDED_MOUSE_BUTTONS 6 + +typedef struct +{ + int x; /* absolute column, 0 based, measured in characters */ + int y; /* absolute row, 0 based, measured in characters */ + short button[3]; /* state of three "normal" buttons */ + int changes; /* flags indicating what has changed with the mouse */ + short xbutton[PDC_N_EXTENDED_MOUSE_BUTTONS]; /* state of ext buttons */ +} MOUSE_STATUS; + +#define BUTTON_RELEASED 0x0000 +#define BUTTON_PRESSED 0x0001 +#define BUTTON_CLICKED 0x0002 +#define BUTTON_DOUBLE_CLICKED 0x0003 +#define BUTTON_TRIPLE_CLICKED 0x0004 +#define BUTTON_MOVED 0x0005 /* PDCurses */ +#define WHEEL_SCROLLED 0x0006 /* PDCurses */ +#define BUTTON_ACTION_MASK 0x0007 /* PDCurses */ + +#define PDC_BUTTON_SHIFT 0x0008 /* PDCurses */ +#define PDC_BUTTON_CONTROL 0x0010 /* PDCurses */ +#define PDC_BUTTON_ALT 0x0020 /* PDCurses */ +#define BUTTON_MODIFIER_MASK 0x0038 /* PDCurses */ + +#define MOUSE_X_POS (Mouse_status.x) +#define MOUSE_Y_POS (Mouse_status.y) + +/* + * Bits associated with the .changes field: + * 3 2 1 0 + * 210987654321098765432109876543210 + * 1 <- button 1 has changed 0 + * 10 <- button 2 has changed 1 + * 100 <- button 3 has changed 2 + * 1000 <- mouse has moved 3 + * 10000 <- mouse position report 4 + * 100000 <- mouse wheel up 5 + * 1000000 <- mouse wheel down 6 + * 10000000 <- mouse wheel left 7 + * 100000000 <- mouse wheel right 8 + * 1000000000 <- button 4 has changed 9 + * (NOTE: buttons 6 to 10000000000 <- button 5 has changed 10 + * 9 aren't implemented 100000000000 <- button 6 has changed 11 + * in any flavor of 1000000000000 <- button 7 has changed 12 + * PDCurses yet!) 10000000000000 <- button 8 has changed 13 + * 100000000000000 <- button 9 has changed 14 + */ + +#define PDC_MOUSE_MOVED 0x0008 +#define PDC_MOUSE_POSITION 0x0010 +#define PDC_MOUSE_WHEEL_UP 0x0020 +#define PDC_MOUSE_WHEEL_DOWN 0x0040 +#define PDC_MOUSE_WHEEL_LEFT 0x0080 +#define PDC_MOUSE_WHEEL_RIGHT 0x0100 + +#define A_BUTTON_CHANGED (Mouse_status.changes & 7) +#define MOUSE_MOVED (Mouse_status.changes & PDC_MOUSE_MOVED) +#define MOUSE_POS_REPORT (Mouse_status.changes & PDC_MOUSE_POSITION) +#define BUTTON_CHANGED(x) (Mouse_status.changes & (1 << ((x) - ((x)<4 ? 1 : -5)))) +#define BUTTON_STATUS(x) (Mouse_status.button[(x) - 1]) +#define MOUSE_WHEEL_UP (Mouse_status.changes & PDC_MOUSE_WHEEL_UP) +#define MOUSE_WHEEL_DOWN (Mouse_status.changes & PDC_MOUSE_WHEEL_DOWN) +#define MOUSE_WHEEL_LEFT (Mouse_status.changes & PDC_MOUSE_WHEEL_LEFT) +#define MOUSE_WHEEL_RIGHT (Mouse_status.changes & PDC_MOUSE_WHEEL_RIGHT) + +/* mouse bit-masks */ + +#define BUTTON1_RELEASED 0x00000001L +#define BUTTON1_PRESSED 0x00000002L +#define BUTTON1_CLICKED 0x00000004L +#define BUTTON1_DOUBLE_CLICKED 0x00000008L +#define BUTTON1_TRIPLE_CLICKED 0x00000010L +#define BUTTON1_MOVED 0x00000010L /* PDCurses */ + +#define BUTTON2_RELEASED 0x00000020L +#define BUTTON2_PRESSED 0x00000040L +#define BUTTON2_CLICKED 0x00000080L +#define BUTTON2_DOUBLE_CLICKED 0x00000100L +#define BUTTON2_TRIPLE_CLICKED 0x00000200L +#define BUTTON2_MOVED 0x00000200L /* PDCurses */ + +#define BUTTON3_RELEASED 0x00000400L +#define BUTTON3_PRESSED 0x00000800L +#define BUTTON3_CLICKED 0x00001000L +#define BUTTON3_DOUBLE_CLICKED 0x00002000L +#define BUTTON3_TRIPLE_CLICKED 0x00004000L +#define BUTTON3_MOVED 0x00004000L /* PDCurses */ + +/* For the ncurses-compatible functions only, BUTTON4_PRESSED and + BUTTON5_PRESSED are returned for mouse scroll wheel up and down; + otherwise PDCurses doesn't support buttons 4 and 5... except + as described above for WinGUI, and perhaps to be extended to + other PDCurses flavors */ + +#define BUTTON4_RELEASED 0x00008000L +#define BUTTON4_PRESSED 0x00010000L +#define BUTTON4_CLICKED 0x00020000L +#define BUTTON4_DOUBLE_CLICKED 0x00040000L +#define BUTTON4_TRIPLE_CLICKED 0x00080000L + +#define BUTTON5_RELEASED 0x00100000L +#define BUTTON5_PRESSED 0x00200000L +#define BUTTON5_CLICKED 0x00400000L +#define BUTTON5_DOUBLE_CLICKED 0x00800000L +#define BUTTON5_TRIPLE_CLICKED 0x01000000L + +#define MOUSE_WHEEL_SCROLL 0x02000000L /* PDCurses */ +#define BUTTON_MODIFIER_SHIFT 0x04000000L /* PDCurses */ +#define BUTTON_MODIFIER_CONTROL 0x08000000L /* PDCurses */ +#define BUTTON_MODIFIER_ALT 0x10000000L /* PDCurses */ + +#define ALL_MOUSE_EVENTS 0x1fffffffL +#define REPORT_MOUSE_POSITION 0x20000000L + +/* ncurses mouse interface */ + +typedef unsigned long mmask_t; + +typedef struct +{ + short id; /* unused, always 0 */ + int x, y, z; /* x, y same as MOUSE_STATUS; z unused */ + mmask_t bstate; /* equivalent to changes + button[], but + in the same format as used for mousemask() */ +} MEVENT; + +#if defined(PDC_NCMOUSE) && !defined(NCURSES_MOUSE_VERSION) +# define NCURSES_MOUSE_VERSION 2 +#endif + +#ifdef NCURSES_MOUSE_VERSION +# define BUTTON_SHIFT BUTTON_MODIFIER_SHIFT +# define BUTTON_CONTROL BUTTON_MODIFIER_CONTROL +# define BUTTON_CTRL BUTTON_MODIFIER_CONTROL +# define BUTTON_ALT BUTTON_MODIFIER_ALT +#else +# define BUTTON_SHIFT PDC_BUTTON_SHIFT +# define BUTTON_CONTROL PDC_BUTTON_CONTROL +# define BUTTON_ALT PDC_BUTTON_ALT +#endif + +/*---------------------------------------------------------------------- + * + * Window and Screen Structures + * + */ + +typedef struct _win /* definition of a window */ +{ + int _cury; /* current pseudo-cursor */ + int _curx; + int _maxy; /* max window coordinates */ + int _maxx; + int _begy; /* origin on screen */ + int _begx; + int _flags; /* window properties */ + chtype _attrs; /* standard attributes and colors */ + chtype _bkgd; /* background, normally blank */ + bool _clear; /* causes clear at next refresh */ + bool _leaveit; /* leaves cursor where it is */ + bool _scroll; /* allows window scrolling */ + bool _nodelay; /* input character wait flag */ + bool _immed; /* immediate update flag */ + bool _sync; /* synchronise window ancestors */ + bool _use_keypad; /* flags keypad key mode active */ + chtype **_y; /* pointer to line pointer array */ + int *_firstch; /* first changed character in line */ + int *_lastch; /* last changed character in line */ + int _tmarg; /* top of scrolling region */ + int _bmarg; /* bottom of scrolling region */ + int _delayms; /* milliseconds of delay for getch() */ + int _parx, _pary; /* coords relative to parent (0,0) */ + struct _win *_parent; /* subwin's pointer to parent win */ +} WINDOW; + +/* Avoid using the SCREEN struct directly -- use the corresponding + functions if possible. This struct may eventually be made private. */ + +typedef struct +{ + bool alive; /* if initscr() called, and not endwin() */ + bool autocr; /* if cr -> lf */ + bool cbreak; /* if terminal unbuffered */ + bool echo; /* if terminal echo */ + bool raw_inp; /* raw input mode (v. cooked input) */ + bool raw_out; /* raw output mode (7 v. 8 bits) */ + bool audible; /* FALSE if the bell is visual */ + bool mono; /* TRUE if current screen is mono */ + bool resized; /* TRUE if TERM has been resized */ + bool orig_attr; /* TRUE if we have the original colors */ + short orig_fore; /* original screen foreground color */ + short orig_back; /* original screen foreground color */ + int cursrow; /* position of physical cursor */ + int curscol; /* position of physical cursor */ + int visibility; /* visibility of cursor */ + int orig_cursor; /* original cursor size */ + int lines; /* new value for LINES */ + int cols; /* new value for COLS */ + unsigned long _trap_mbe; /* trap these mouse button events */ + unsigned long _map_mbe_to_key; /* map mouse buttons to slk */ + int mouse_wait; /* time to wait (in ms) for a + button release after a press, in + order to count it as a click */ + int slklines; /* lines in use by slk_init() */ + WINDOW *slk_winptr; /* window for slk */ + int linesrippedoff; /* lines ripped off via ripoffline() */ + int linesrippedoffontop; /* lines ripped off on + top via ripoffline() */ + int delaytenths; /* 1/10ths second to wait block + getch() for */ + bool _preserve; /* TRUE if screen background + to be preserved */ + int _restore; /* specifies if screen background + to be restored, and how */ + bool save_key_modifiers; /* TRUE if each key modifiers saved + with each key press */ + bool return_key_modifiers; /* TRUE if modifier keys are + returned as "real" keys */ + bool key_code; /* TRUE if last key is a special key; + used internally by get_wch() */ +#ifdef XCURSES + int XcurscrSize; /* size of Xcurscr shared memory block */ + bool sb_on; + int sb_viewport_y; + int sb_viewport_x; + int sb_total_y; + int sb_total_x; + int sb_cur_y; + int sb_cur_x; + int exit_key; +#endif + short line_color; /* color of line attributes - default -1 */ +} SCREEN; + +/*---------------------------------------------------------------------- + * + * External Variables + * + */ + +#ifdef PDC_DLL_BUILD +# ifdef CURSES_LIBRARY +# define PDCEX __declspec(dllexport) extern +# else +# define PDCEX __declspec(dllimport) +# endif +#else +# define PDCEX extern +#endif + +PDCEX int LINES; /* terminal height */ +PDCEX int COLS; /* terminal width */ +PDCEX WINDOW *stdscr; /* the default screen window */ +PDCEX WINDOW *curscr; /* the current screen image */ +PDCEX SCREEN *SP; /* curses variables */ +PDCEX MOUSE_STATUS Mouse_status; +PDCEX int COLORS; +PDCEX int COLOR_PAIRS; +PDCEX int TABSIZE; +PDCEX chtype acs_map[]; /* alternate character set map */ +PDCEX char ttytype[]; /* terminal name/description */ +PDCEX PDC_version_info PDC_version; + +/*man-start************************************************************** + +Text Attributes +=============== + +Originally, PDCurses used a short (16 bits) for its chtype. To include +color, a number of things had to be sacrificed from the strict Unix and +System V support. The main problem was fitting all character attributes +and color into an unsigned char (all 8 bits!). + +Today, PDCurses by default uses a long (32 bits) for its chtype, as in +System V. The short chtype is still available, by undefining CHTYPE_LONG +and rebuilding the library. + +The following is the structure of a win->_attrs chtype: + +short form: + + +-----------------------------------------------+ + |15|14|13|12|11|10| 9| 8| 7| 6| 5| 4| 3| 2| 1| 0| + +-----------------------------------------------+ + color number | attrs | character eg 'a' + +The available non-color attributes are bold, reverse and blink. Others +have no effect. The high order char is an index into an array of +physical colors (defined in color.c) -- 32 foreground/background color +pairs (5 bits) plus 3 bits for other attributes. + +long form: + + +--------------------------------------------------------------------+ + |31|30|29|28|27|26|25|24|23|22|21|20|19|18|17|16|15|14|13|..| 2| 1| 0| + +--------------------------------------------------------------------+ + color number | modifiers | character eg 'a' + +The available non-color attributes are bold, underline, invisible, +right-line, left-line, protect, reverse and blink. 256 color pairs (8 +bits), 8 bits for other attributes, and 16 bits for character data. + + Note that there is now a "super-long" 64-bit form, available by +defining CHTYPE_LONG to be 2: + +------------------------------------------------------------------------------- +|63|62|61|60|59|..|34|33|32|31|30|29|28|..|22|21|20|19|18|17|16|..| 3| 2| 1| 0| +------------------------------------------------------------------------------- + color number | modifiers | character eg 'a' + + + We take five more bits for the character (thus allowing Unicode values +past 64K; UTF-16 can go up to 0x10ffff, requiring 21 bits total), and +four more bits for attributes. Three are currently used as A_OVERLINE, A_DIM, +and A_STRIKEOUT; one more is reserved for future use. 31 bits are then used +for color. These are usually just treated as the usual palette +indices, and range from 0 to 255. However, if bit 63 is +set, the remaining 30 bits are interpreted as foreground RGB (first +fifteen bits, five bits for each of the three channels) and background RGB +(same scheme using the remaining 15 bits.) + +**man-end****************************************************************/ + +/*** Video attribute macros ***/ + +#define A_NORMAL (chtype)0 + +#ifdef CHTYPE_LONG + +# if(CHTYPE_LONG >= 2) /* 64-bit chtypes */ + # define PDC_CHARTEXT_BITS 21 + # define A_CHARTEXT (chtype)( ((chtype)0x1 << PDC_CHARTEXT_BITS) - 1) + # define A_ALTCHARSET ((chtype)0x001 << PDC_CHARTEXT_BITS) + # define A_RIGHTLINE ((chtype)0x002 << PDC_CHARTEXT_BITS) + # define A_LEFTLINE ((chtype)0x004 << PDC_CHARTEXT_BITS) + # define A_INVIS ((chtype)0x008 << PDC_CHARTEXT_BITS) + # define A_UNDERLINE ((chtype)0x010 << PDC_CHARTEXT_BITS) + # define A_REVERSE ((chtype)0x020 << PDC_CHARTEXT_BITS) + # define A_BLINK ((chtype)0x040 << PDC_CHARTEXT_BITS) + # define A_BOLD ((chtype)0x080 << PDC_CHARTEXT_BITS) + # define A_OVERLINE ((chtype)0x100 << PDC_CHARTEXT_BITS) + # define A_STRIKEOUT ((chtype)0x200 << PDC_CHARTEXT_BITS) + # define A_DIM ((chtype)0x400 << PDC_CHARTEXT_BITS) +#if 0 + /* May come up with a use for this bit */ + /* someday; reserved for the future: */ + # define A_FUTURE_2 ((chtype)0x800 << PDC_CHARTEXT_BITS) +#endif + # define PDC_COLOR_SHIFT (PDC_CHARTEXT_BITS + 12) + # define A_COLOR ((chtype)0x7fffffff << PDC_COLOR_SHIFT) + # define A_RGB_COLOR ((chtype)0x40000000 << PDC_COLOR_SHIFT) + # define A_ATTRIBUTES (((chtype)0xfff << PDC_CHARTEXT_BITS) | A_COLOR) + # define A_RGB( rfore, gfore, bfore, rback, gback, bback) \ + (( (((chtype)(bfore) << 25) \ + | ((chtype)(gfore) << 20) \ + | ((chtype)(rfore) << 15) \ + | ((chtype)(bback) << 10) \ + | ((chtype)(gback) << 5) \ + | ((chtype)(rback) )) << PDC_COLOR_SHIFT) | A_RGB_COLOR) +# else /* plain ol' 32-bit chtypes */ + # define A_ALTCHARSET (chtype)0x00010000 + # define A_RIGHTLINE (chtype)0x00020000 + # define A_LEFTLINE (chtype)0x00040000 + # define A_INVIS (chtype)0x00080000 + # define A_UNDERLINE (chtype)0x00100000 + # define A_REVERSE (chtype)0x00200000 + # define A_BLINK (chtype)0x00400000 + # define A_BOLD (chtype)0x00800000 + # define A_COLOR (chtype)0xff000000 + # define A_RGB_COLOR A_NORMAL +#ifdef PDC_WIDE + # define A_CHARTEXT (chtype)0x0000ffff + # define A_ATTRIBUTES (chtype)0xffff0000 + # define A_DIM A_NORMAL + # define A_OVERLINE A_NORMAL + # define A_STRIKEOUT A_NORMAL +#else /* with 8-bit chars, we have bits for these attribs : */ + # define A_CHARTEXT (chtype)0x000000ff + # define A_ATTRIBUTES (chtype)0xffffe000 + # define A_DIM (chtype)0x00008000 + # define A_OVERLINE (chtype)0x00004000 + # define A_STRIKEOUT (chtype)0x00002000 +#endif + # define PDC_COLOR_SHIFT 24 +#endif + + +# define A_ITALIC A_INVIS +# define A_PROTECT (A_UNDERLINE | A_LEFTLINE | A_RIGHTLINE) + +#else /* 16-bit chtypes */ +# define A_BOLD (chtype)0x0100 /* X/Open */ +# define A_REVERSE (chtype)0x0200 /* X/Open */ +# define A_BLINK (chtype)0x0400 /* X/Open */ + +# define A_ATTRIBUTES (chtype)0xff00 /* X/Open */ +# define A_CHARTEXT (chtype)0x00ff /* X/Open */ +# define A_COLOR (chtype)0xf800 /* System V */ + +# define A_ALTCHARSET A_NORMAL /* X/Open */ +# define A_PROTECT A_NORMAL /* X/Open */ +# define A_UNDERLINE A_NORMAL /* X/Open */ +# define A_OVERLINE A_NORMAL /* X/Open */ +# define A_STRIKEOUT A_NORMAL /* X/Open */ + +# define A_LEFTLINE A_NORMAL +# define A_RIGHTLINE A_NORMAL +# define A_ITALIC A_NORMAL +# define A_INVIS A_NORMAL +# define A_RGB_COLOR A_NORMAL +# define A_DIM A_NORMAL + +# define PDC_COLOR_SHIFT 11 +#endif + +#define A_STANDOUT (A_REVERSE | A_BOLD) /* X/Open */ + +#define CHR_MSK A_CHARTEXT /* Obsolete */ +#define ATR_MSK A_ATTRIBUTES /* Obsolete */ +#define ATR_NRM A_NORMAL /* Obsolete */ + +/* For use with attr_t -- X/Open says, "these shall be distinct", so + this is a non-conforming implementation. */ + +#define WA_NORMAL A_NORMAL + +#define WA_ALTCHARSET A_ALTCHARSET +#define WA_BLINK A_BLINK +#define WA_BOLD A_BOLD +#define WA_DIM A_DIM +#define WA_INVIS A_INVIS +#define WA_LEFT A_LEFTLINE +#define WA_PROTECT A_PROTECT +#define WA_REVERSE A_REVERSE +#define WA_RIGHT A_RIGHTLINE +#define WA_STANDOUT A_STANDOUT +#define WA_UNDERLINE A_UNDERLINE + +#define WA_HORIZONTAL A_NORMAL +#define WA_LOW A_NORMAL +#define WA_TOP A_NORMAL +#define WA_VERTICAL A_NORMAL + +#define WA_ATTRIBUTES A_ATTRIBUTES + +/*** Alternate character set macros ***/ + +/* 'w' = 32-bit chtype; acs_map[] index | A_ALTCHARSET + 'n' = 16-bit chtype; it gets the fallback set because no bit is + available for A_ALTCHARSET */ + +#ifdef CHTYPE_LONG +# define PDC_ACS(w, n) ((chtype)w | A_ALTCHARSET) +#else +# define PDC_ACS(w, n) ((chtype)n) +#endif + +/* VT100-compatible symbols -- box chars */ + +#define ACS_LRCORNER PDC_ACS('V', '+') +#define ACS_URCORNER PDC_ACS('W', '+') +#define ACS_ULCORNER PDC_ACS('X', '+') +#define ACS_LLCORNER PDC_ACS('Y', '+') +#define ACS_PLUS PDC_ACS('Z', '+') +#define ACS_LTEE PDC_ACS('[', '+') +#define ACS_RTEE PDC_ACS('\\', '+') +#define ACS_BTEE PDC_ACS(']', '+') +#define ACS_TTEE PDC_ACS('^', '+') +#define ACS_HLINE PDC_ACS('_', '-') +#define ACS_VLINE PDC_ACS('`', '|') + +/* PDCurses-only ACS chars. Don't use if ncurses compatibility matters. +Some won't work in non-wide X11 builds (see 'acs_defs.h' for details). */ + +#define ACS_CENT PDC_ACS('{', 'c') +#define ACS_YEN PDC_ACS('|', 'y') +#define ACS_PESETA PDC_ACS('}', 'p') +#define ACS_HALF PDC_ACS('&', '/') +#define ACS_QUARTER PDC_ACS('\'', '/') +#define ACS_LEFT_ANG_QU PDC_ACS(')', '<') +#define ACS_RIGHT_ANG_QU PDC_ACS('*', '>') +#define ACS_D_HLINE PDC_ACS('a', '-') +#define ACS_D_VLINE PDC_ACS('b', '|') +#define ACS_CLUB PDC_ACS( 11, 'C') +#define ACS_HEART PDC_ACS( 12, 'H') +#define ACS_SPADE PDC_ACS( 13, 'S') +#define ACS_SMILE PDC_ACS( 14, 'O') +#define ACS_REV_SMILE PDC_ACS( 15, 'O') +#define ACS_MED_BULLET PDC_ACS( 16, '.') +#define ACS_WHITE_BULLET PDC_ACS( 17, 'O') +#define ACS_PILCROW PDC_ACS( 18, 'O') +#define ACS_SECTION PDC_ACS( 19, 'O') + +#define ACS_SUP2 PDC_ACS(',', '2') +#define ACS_ALPHA PDC_ACS('.', 'a') +#define ACS_BETA PDC_ACS('/', 'b') +#define ACS_GAMMA PDC_ACS('0', 'y') +#define ACS_UP_SIGMA PDC_ACS('1', 'S') +#define ACS_LO_SIGMA PDC_ACS('2', 's') +#define ACS_MU PDC_ACS('4', 'u') +#define ACS_TAU PDC_ACS('5', 't') +#define ACS_UP_PHI PDC_ACS('6', 'F') +#define ACS_THETA PDC_ACS('7', 't') +#define ACS_OMEGA PDC_ACS('8', 'w') +#define ACS_DELTA PDC_ACS('9', 'd') +#define ACS_INFINITY PDC_ACS('-', 'i') +#define ACS_LO_PHI PDC_ACS( 22, 'f') +#define ACS_EPSILON PDC_ACS(':', 'e') +#define ACS_INTERSECT PDC_ACS('e', 'u') +#define ACS_TRIPLE_BAR PDC_ACS('f', '=') +#define ACS_DIVISION PDC_ACS('c', '/') +#define ACS_APPROX_EQ PDC_ACS('d', '~') +#define ACS_SM_BULLET PDC_ACS('g', '.') +#define ACS_SQUARE_ROOT PDC_ACS('i', '!') +#define ACS_UBLOCK PDC_ACS('p', '^') +#define ACS_BBLOCK PDC_ACS('q', '_') +#define ACS_LBLOCK PDC_ACS('r', '<') +#define ACS_RBLOCK PDC_ACS('s', '>') + +#define ACS_A_ORDINAL PDC_ACS(20, 'a') +#define ACS_O_ORDINAL PDC_ACS(21, 'o') +#define ACS_INV_QUERY PDC_ACS(24, '?') +#define ACS_REV_NOT PDC_ACS(25, '!') +#define ACS_NOT PDC_ACS(26, '!') +#define ACS_INV_BANG PDC_ACS(23, '!') +#define ACS_UP_INTEGRAL PDC_ACS(27, '|') +#define ACS_LO_INTEGRAL PDC_ACS(28, '|') +#define ACS_SUP_N PDC_ACS(29, 'n') +#define ACS_CENTER_SQU PDC_ACS(30, 'x') +#define ACS_F_WITH_HOOK PDC_ACS(31, 'f') + +#define ACS_SD_LRCORNER PDC_ACS(';', '+') +#define ACS_SD_URCORNER PDC_ACS('<', '+') +#define ACS_SD_ULCORNER PDC_ACS('=', '+') +#define ACS_SD_LLCORNER PDC_ACS('>', '+') +#define ACS_SD_PLUS PDC_ACS('?', '+') +#define ACS_SD_LTEE PDC_ACS('@', '+') +#define ACS_SD_RTEE PDC_ACS('A', '+') +#define ACS_SD_BTEE PDC_ACS('B', '+') +#define ACS_SD_TTEE PDC_ACS('C', '+') + +#define ACS_D_LRCORNER PDC_ACS('D', '+') +#define ACS_D_URCORNER PDC_ACS('E', '+') +#define ACS_D_ULCORNER PDC_ACS('F', '+') +#define ACS_D_LLCORNER PDC_ACS('G', '+') +#define ACS_D_PLUS PDC_ACS('H', '+') +#define ACS_D_LTEE PDC_ACS('I', '+') +#define ACS_D_RTEE PDC_ACS('J', '+') +#define ACS_D_BTEE PDC_ACS('K', '+') +#define ACS_D_TTEE PDC_ACS('L', '+') + +#define ACS_DS_LRCORNER PDC_ACS('M', '+') +#define ACS_DS_URCORNER PDC_ACS('N', '+') +#define ACS_DS_ULCORNER PDC_ACS('O', '+') +#define ACS_DS_LLCORNER PDC_ACS('P', '+') +#define ACS_DS_PLUS PDC_ACS('Q', '+') +#define ACS_DS_LTEE PDC_ACS('R', '+') +#define ACS_DS_RTEE PDC_ACS('S', '+') +#define ACS_DS_BTEE PDC_ACS('T', '+') +#define ACS_DS_TTEE PDC_ACS('U', '+') + +/* VT100-compatible symbols -- other */ + +#define ACS_S1 PDC_ACS('l', '-') +#define ACS_S9 PDC_ACS('o', '_') +#define ACS_DIAMOND PDC_ACS('j', '+') +#define ACS_CKBOARD PDC_ACS('k', ':') +#define ACS_DEGREE PDC_ACS('w', '\'') +#define ACS_PLMINUS PDC_ACS('x', '#') +#define ACS_BULLET PDC_ACS('h', 'o') + +/* Teletype 5410v1 symbols -- these are defined in SysV curses, but + are not well-supported by most terminals. Stick to VT100 characters + for optimum portability. */ + +#define ACS_LARROW PDC_ACS('!', '<') +#define ACS_RARROW PDC_ACS(' ', '>') +#define ACS_DARROW PDC_ACS('#', 'v') +#define ACS_UARROW PDC_ACS('"', '^') +#define ACS_BOARD PDC_ACS('+', '#') +#define ACS_LTBOARD PDC_ACS('y', '#') +#define ACS_LANTERN PDC_ACS('z', '*') +#define ACS_BLOCK PDC_ACS('t', '#') + +/* That goes double for these -- undocumented SysV symbols. Don't use + them. */ + +#define ACS_S3 PDC_ACS('m', '-') +#define ACS_S7 PDC_ACS('n', '-') +#define ACS_LEQUAL PDC_ACS('u', '<') +#define ACS_GEQUAL PDC_ACS('v', '>') +#define ACS_PI PDC_ACS('$', 'n') +#define ACS_NEQUAL PDC_ACS('%', '+') +#define ACS_STERLING PDC_ACS('~', 'L') + +/* Box char aliases */ + +#define ACS_BSSB ACS_ULCORNER +#define ACS_SSBB ACS_LLCORNER +#define ACS_BBSS ACS_URCORNER +#define ACS_SBBS ACS_LRCORNER +#define ACS_SBSS ACS_RTEE +#define ACS_SSSB ACS_LTEE +#define ACS_SSBS ACS_BTEE +#define ACS_BSSS ACS_TTEE +#define ACS_BSBS ACS_HLINE +#define ACS_SBSB ACS_VLINE +#define ACS_SSSS ACS_PLUS + +/* cchar_t aliases */ + +#ifdef PDC_WIDE +# define WACS_LRCORNER (&(acs_map['V'])) +# define WACS_URCORNER (&(acs_map['W'])) +# define WACS_ULCORNER (&(acs_map['X'])) +# define WACS_LLCORNER (&(acs_map['Y'])) +# define WACS_PLUS (&(acs_map['Z'])) +# define WACS_LTEE (&(acs_map['['])) +# define WACS_RTEE (&(acs_map['\\'])) +# define WACS_BTEE (&(acs_map[']'])) +# define WACS_TTEE (&(acs_map['^'])) +# define WACS_HLINE (&(acs_map['_'])) +# define WACS_VLINE (&(acs_map['`'])) + +# define WACS_CENT (&(acs_map['{'])) +# define WACS_YEN (&(acs_map['|'])) +# define WACS_PESETA (&(acs_map['}'])) +# define WACS_HALF (&(acs_map['&'])) +# define WACS_QUARTER (&(acs_map['\''])) +# define WACS_LEFT_ANG_QU (&(acs_map[')'])) +# define WACS_RIGHT_ANG_QU (&(acs_map['*'])) +# define WACS_D_HLINE (&(acs_map['a'])) +# define WACS_D_VLINE (&(acs_map['b'])) +# define WACS_CLUB (&(acs_map[ 11])) +# define WACS_HEART (&(acs_map[ 12])) +# define WACS_SPADE (&(acs_map[ 13])) +# define WACS_SMILE (&(acs_map[ 14])) +# define WACS_REV_SMILE (&(acs_map[ 15])) +# define WACS_MED_BULLET (&(acs_map[ 16])) +# define WACS_WHITE_BULLET (&(acs_map[ 17])) +# define WACS_PILCROW (&(acs_map[ 18])) +# define WACS_SECTION (&(acs_map[ 19])) + +# define WACS_SUP2 (&(acs_map[','])) +# define WACS_ALPHA (&(acs_map['.'])) +# define WACS_BETA (&(acs_map['/'])) +# define WACS_GAMMA (&(acs_map['0'])) +# define WACS_UP_SIGMA (&(acs_map['1'])) +# define WACS_LO_SIGMA (&(acs_map['2'])) +# define WACS_MU (&(acs_map['4'])) +# define WACS_TAU (&(acs_map['5'])) +# define WACS_UP_PHI (&(acs_map['6'])) +# define WACS_THETA (&(acs_map['7'])) +# define WACS_OMEGA (&(acs_map['8'])) +# define WACS_DELTA (&(acs_map['9'])) +# define WACS_INFINITY (&(acs_map['-'])) +# define WACS_LO_PHI (&(acs_map[ 22])) +# define WACS_EPSILON (&(acs_map[':'])) +# define WACS_INTERSECT (&(acs_map['e'])) +# define WACS_TRIPLE_BAR (&(acs_map['f'])) +# define WACS_DIVISION (&(acs_map['c'])) +# define WACS_APPROX_EQ (&(acs_map['d'])) +# define WACS_SM_BULLET (&(acs_map['g'])) +# define WACS_SQUARE_ROOT (&(acs_map['i'])) +# define WACS_UBLOCK (&(acs_map['p'])) +# define WACS_BBLOCK (&(acs_map['q'])) +# define WACS_LBLOCK (&(acs_map['r'])) +# define WACS_RBLOCK (&(acs_map['s'])) + +# define WACS_A_ORDINAL (&(acs_map[20])) +# define WACS_O_ORDINAL (&(acs_map[21])) +# define WACS_INV_QUERY (&(acs_map[24])) +# define WACS_REV_NOT (&(acs_map[25])) +# define WACS_NOT (&(acs_map[26])) +# define WACS_INV_BANG (&(acs_map[23])) +# define WACS_UP_INTEGRAL (&(acs_map[27])) +# define WACS_LO_INTEGRAL (&(acs_map[28])) +# define WACS_SUP_N (&(acs_map[29])) +# define WACS_CENTER_SQU (&(acs_map[30])) +# define WACS_F_WITH_HOOK (&(acs_map[31])) + +# define WACS_SD_LRCORNER (&(acs_map[';'])) +# define WACS_SD_URCORNER (&(acs_map['<'])) +# define WACS_SD_ULCORNER (&(acs_map['='])) +# define WACS_SD_LLCORNER (&(acs_map['>'])) +# define WACS_SD_PLUS (&(acs_map['?'])) +# define WACS_SD_LTEE (&(acs_map['@'])) +# define WACS_SD_RTEE (&(acs_map['A'])) +# define WACS_SD_BTEE (&(acs_map['B'])) +# define WACS_SD_TTEE (&(acs_map['C'])) + +# define WACS_D_LRCORNER (&(acs_map['D'])) +# define WACS_D_URCORNER (&(acs_map['E'])) +# define WACS_D_ULCORNER (&(acs_map['F'])) +# define WACS_D_LLCORNER (&(acs_map['G'])) +# define WACS_D_PLUS (&(acs_map['H'])) +# define WACS_D_LTEE (&(acs_map['I'])) +# define WACS_D_RTEE (&(acs_map['J'])) +# define WACS_D_BTEE (&(acs_map['K'])) +# define WACS_D_TTEE (&(acs_map['L'])) + +# define WACS_DS_LRCORNER (&(acs_map['M'])) +# define WACS_DS_URCORNER (&(acs_map['N'])) +# define WACS_DS_ULCORNER (&(acs_map['O'])) +# define WACS_DS_LLCORNER (&(acs_map['P'])) +# define WACS_DS_PLUS (&(acs_map['Q'])) +# define WACS_DS_LTEE (&(acs_map['R'])) +# define WACS_DS_RTEE (&(acs_map['S'])) +# define WACS_DS_BTEE (&(acs_map['T'])) +# define WACS_DS_TTEE (&(acs_map['U'])) + +# define WACS_S1 (&(acs_map['l'])) +# define WACS_S9 (&(acs_map['o'])) +# define WACS_DIAMOND (&(acs_map['j'])) +# define WACS_CKBOARD (&(acs_map['k'])) +# define WACS_DEGREE (&(acs_map['w'])) +# define WACS_PLMINUS (&(acs_map['x'])) +# define WACS_BULLET (&(acs_map['h'])) + + +# define WACS_LARROW (&(acs_map['!'])) +# define WACS_RARROW (&(acs_map[' '])) +# define WACS_DARROW (&(acs_map['#'])) +# define WACS_UARROW (&(acs_map['"'])) +# define WACS_BOARD (&(acs_map['+'])) +# define WACS_LTBOARD (&(acs_map['y'])) +# define WACS_LANTERN (&(acs_map['z'])) +# define WACS_BLOCK (&(acs_map['t'])) + +# define WACS_S3 (&(acs_map['m'])) +# define WACS_S7 (&(acs_map['n'])) +# define WACS_LEQUAL (&(acs_map['u'])) +# define WACS_GEQUAL (&(acs_map['v'])) +# define WACS_PI (&(acs_map['$'])) +# define WACS_NEQUAL (&(acs_map['%'])) +# define WACS_STERLING (&(acs_map['~'])) + +# define WACS_BSSB WACS_ULCORNER +# define WACS_SSBB WACS_LLCORNER +# define WACS_BBSS WACS_URCORNER +# define WACS_SBBS WACS_LRCORNER +# define WACS_SBSS WACS_RTEE +# define WACS_SSSB WACS_LTEE +# define WACS_SSBS WACS_BTEE +# define WACS_BSSS WACS_TTEE +# define WACS_BSBS WACS_HLINE +# define WACS_SBSB WACS_VLINE +# define WACS_SSSS WACS_PLUS +#endif + +/*** Color macros ***/ + +#define COLOR_BLACK 0 + +#ifdef PDC_RGB /* RGB */ +# define COLOR_RED 1 +# define COLOR_GREEN 2 +# define COLOR_BLUE 4 +#else /* BGR */ +# define COLOR_BLUE 1 +# define COLOR_GREEN 2 +# define COLOR_RED 4 +#endif + +#define COLOR_CYAN (COLOR_BLUE | COLOR_GREEN) +#define COLOR_MAGENTA (COLOR_RED | COLOR_BLUE) +#define COLOR_YELLOW (COLOR_RED | COLOR_GREEN) + +#define COLOR_WHITE 7 + +/*---------------------------------------------------------------------- + * + * Function and Keypad Key Definitions + * Many are just for compatibility + * + */ + +#ifdef PDC_WIDE + #define KEY_OFFSET 0xec00 +#else + #define KEY_OFFSET 0x100 +#endif + +#define KEY_CODE_YES (KEY_OFFSET + 0x00) /* If get_wch() gives a key code */ + +#define KEY_BREAK (KEY_OFFSET + 0x01) /* Not on PC KBD */ +#define KEY_DOWN (KEY_OFFSET + 0x02) /* Down arrow key */ +#define KEY_UP (KEY_OFFSET + 0x03) /* Up arrow key */ +#define KEY_LEFT (KEY_OFFSET + 0x04) /* Left arrow key */ +#define KEY_RIGHT (KEY_OFFSET + 0x05) /* Right arrow key */ +#define KEY_HOME (KEY_OFFSET + 0x06) /* home key */ +#define KEY_BACKSPACE (KEY_OFFSET + 0x07) /* not on pc */ +#define KEY_F0 (KEY_OFFSET + 0x08) /* function keys; 64 reserved */ + +#define KEY_DL (KEY_OFFSET + 0x48) /* delete line */ +#define KEY_IL (KEY_OFFSET + 0x49) /* insert line */ +#define KEY_DC (KEY_OFFSET + 0x4a) /* delete character */ +#define KEY_IC (KEY_OFFSET + 0x4b) /* insert char or enter ins mode */ +#define KEY_EIC (KEY_OFFSET + 0x4c) /* exit insert char mode */ +#define KEY_CLEAR (KEY_OFFSET + 0x4d) /* clear screen */ +#define KEY_EOS (KEY_OFFSET + 0x4e) /* clear to end of screen */ +#define KEY_EOL (KEY_OFFSET + 0x4f) /* clear to end of line */ +#define KEY_SF (KEY_OFFSET + 0x50) /* scroll 1 line forward */ +#define KEY_SR (KEY_OFFSET + 0x51) /* scroll 1 line back (reverse) */ +#define KEY_NPAGE (KEY_OFFSET + 0x52) /* next page */ +#define KEY_PPAGE (KEY_OFFSET + 0x53) /* previous page */ +#define KEY_STAB (KEY_OFFSET + 0x54) /* set tab */ +#define KEY_CTAB (KEY_OFFSET + 0x55) /* clear tab */ +#define KEY_CATAB (KEY_OFFSET + 0x56) /* clear all tabs */ +#define KEY_ENTER (KEY_OFFSET + 0x57) /* enter or send (unreliable) */ +#define KEY_SRESET (KEY_OFFSET + 0x58) /* soft/reset (partial/unreliable) */ +#define KEY_RESET (KEY_OFFSET + 0x59) /* reset/hard reset (unreliable) */ +#define KEY_PRINT (KEY_OFFSET + 0x5a) /* print/copy */ +#define KEY_LL (KEY_OFFSET + 0x5b) /* home down/bottom (lower left) */ +#define KEY_ABORT (KEY_OFFSET + 0x5c) /* abort/terminate key (any) */ +#define KEY_SHELP (KEY_OFFSET + 0x5d) /* short help */ +#define KEY_LHELP (KEY_OFFSET + 0x5e) /* long help */ +#define KEY_BTAB (KEY_OFFSET + 0x5f) /* Back tab key */ +#define KEY_BEG (KEY_OFFSET + 0x60) /* beg(inning) key */ +#define KEY_CANCEL (KEY_OFFSET + 0x61) /* cancel key */ +#define KEY_CLOSE (KEY_OFFSET + 0x62) /* close key */ +#define KEY_COMMAND (KEY_OFFSET + 0x63) /* cmd (command) key */ +#define KEY_COPY (KEY_OFFSET + 0x64) /* copy key */ +#define KEY_CREATE (KEY_OFFSET + 0x65) /* create key */ +#define KEY_END (KEY_OFFSET + 0x66) /* end key */ +#define KEY_EXIT (KEY_OFFSET + 0x67) /* exit key */ +#define KEY_FIND (KEY_OFFSET + 0x68) /* find key */ +#define KEY_HELP (KEY_OFFSET + 0x69) /* help key */ +#define KEY_MARK (KEY_OFFSET + 0x6a) /* mark key */ +#define KEY_MESSAGE (KEY_OFFSET + 0x6b) /* message key */ +#define KEY_MOVE (KEY_OFFSET + 0x6c) /* move key */ +#define KEY_NEXT (KEY_OFFSET + 0x6d) /* next object key */ +#define KEY_OPEN (KEY_OFFSET + 0x6e) /* open key */ +#define KEY_OPTIONS (KEY_OFFSET + 0x6f) /* options key */ +#define KEY_PREVIOUS (KEY_OFFSET + 0x70) /* previous object key */ +#define KEY_REDO (KEY_OFFSET + 0x71) /* redo key */ +#define KEY_REFERENCE (KEY_OFFSET + 0x72) /* ref(erence) key */ +#define KEY_REFRESH (KEY_OFFSET + 0x73) /* refresh key */ +#define KEY_REPLACE (KEY_OFFSET + 0x74) /* replace key */ +#define KEY_RESTART (KEY_OFFSET + 0x75) /* restart key */ +#define KEY_RESUME (KEY_OFFSET + 0x76) /* resume key */ +#define KEY_SAVE (KEY_OFFSET + 0x77) /* save key */ +#define KEY_SBEG (KEY_OFFSET + 0x78) /* shifted beginning key */ +#define KEY_SCANCEL (KEY_OFFSET + 0x79) /* shifted cancel key */ +#define KEY_SCOMMAND (KEY_OFFSET + 0x7a) /* shifted command key */ +#define KEY_SCOPY (KEY_OFFSET + 0x7b) /* shifted copy key */ +#define KEY_SCREATE (KEY_OFFSET + 0x7c) /* shifted create key */ +#define KEY_SDC (KEY_OFFSET + 0x7d) /* shifted delete char key */ +#define KEY_SDL (KEY_OFFSET + 0x7e) /* shifted delete line key */ +#define KEY_SELECT (KEY_OFFSET + 0x7f) /* select key */ +#define KEY_SEND (KEY_OFFSET + 0x80) /* shifted end key */ +#define KEY_SEOL (KEY_OFFSET + 0x81) /* shifted clear line key */ +#define KEY_SEXIT (KEY_OFFSET + 0x82) /* shifted exit key */ +#define KEY_SFIND (KEY_OFFSET + 0x83) /* shifted find key */ +#define KEY_SHOME (KEY_OFFSET + 0x84) /* shifted home key */ +#define KEY_SIC (KEY_OFFSET + 0x85) /* shifted input key */ + +#define KEY_SLEFT (KEY_OFFSET + 0x87) /* shifted left arrow key */ +#define KEY_SMESSAGE (KEY_OFFSET + 0x88) /* shifted message key */ +#define KEY_SMOVE (KEY_OFFSET + 0x89) /* shifted move key */ +#define KEY_SNEXT (KEY_OFFSET + 0x8a) /* shifted next key */ +#define KEY_SOPTIONS (KEY_OFFSET + 0x8b) /* shifted options key */ +#define KEY_SPREVIOUS (KEY_OFFSET + 0x8c) /* shifted prev key */ +#define KEY_SPRINT (KEY_OFFSET + 0x8d) /* shifted print key */ +#define KEY_SREDO (KEY_OFFSET + 0x8e) /* shifted redo key */ +#define KEY_SREPLACE (KEY_OFFSET + 0x8f) /* shifted replace key */ +#define KEY_SRIGHT (KEY_OFFSET + 0x90) /* shifted right arrow */ +#define KEY_SRSUME (KEY_OFFSET + 0x91) /* shifted resume key */ +#define KEY_SSAVE (KEY_OFFSET + 0x92) /* shifted save key */ +#define KEY_SSUSPEND (KEY_OFFSET + 0x93) /* shifted suspend key */ +#define KEY_SUNDO (KEY_OFFSET + 0x94) /* shifted undo key */ +#define KEY_SUSPEND (KEY_OFFSET + 0x95) /* suspend key */ +#define KEY_UNDO (KEY_OFFSET + 0x96) /* undo key */ + +/* PDCurses-specific key definitions -- PC only */ + +#define ALT_0 (KEY_OFFSET + 0x97) +#define ALT_1 (KEY_OFFSET + 0x98) +#define ALT_2 (KEY_OFFSET + 0x99) +#define ALT_3 (KEY_OFFSET + 0x9a) +#define ALT_4 (KEY_OFFSET + 0x9b) +#define ALT_5 (KEY_OFFSET + 0x9c) +#define ALT_6 (KEY_OFFSET + 0x9d) +#define ALT_7 (KEY_OFFSET + 0x9e) +#define ALT_8 (KEY_OFFSET + 0x9f) +#define ALT_9 (KEY_OFFSET + 0xa0) +#define ALT_A (KEY_OFFSET + 0xa1) +#define ALT_B (KEY_OFFSET + 0xa2) +#define ALT_C (KEY_OFFSET + 0xa3) +#define ALT_D (KEY_OFFSET + 0xa4) +#define ALT_E (KEY_OFFSET + 0xa5) +#define ALT_F (KEY_OFFSET + 0xa6) +#define ALT_G (KEY_OFFSET + 0xa7) +#define ALT_H (KEY_OFFSET + 0xa8) +#define ALT_I (KEY_OFFSET + 0xa9) +#define ALT_J (KEY_OFFSET + 0xaa) +#define ALT_K (KEY_OFFSET + 0xab) +#define ALT_L (KEY_OFFSET + 0xac) +#define ALT_M (KEY_OFFSET + 0xad) +#define ALT_N (KEY_OFFSET + 0xae) +#define ALT_O (KEY_OFFSET + 0xaf) +#define ALT_P (KEY_OFFSET + 0xb0) +#define ALT_Q (KEY_OFFSET + 0xb1) +#define ALT_R (KEY_OFFSET + 0xb2) +#define ALT_S (KEY_OFFSET + 0xb3) +#define ALT_T (KEY_OFFSET + 0xb4) +#define ALT_U (KEY_OFFSET + 0xb5) +#define ALT_V (KEY_OFFSET + 0xb6) +#define ALT_W (KEY_OFFSET + 0xb7) +#define ALT_X (KEY_OFFSET + 0xb8) +#define ALT_Y (KEY_OFFSET + 0xb9) +#define ALT_Z (KEY_OFFSET + 0xba) + +#define CTL_LEFT (KEY_OFFSET + 0xbb) /* Control-Left-Arrow */ +#define CTL_RIGHT (KEY_OFFSET + 0xbc) +#define CTL_PGUP (KEY_OFFSET + 0xbd) +#define CTL_PGDN (KEY_OFFSET + 0xbe) +#define CTL_HOME (KEY_OFFSET + 0xbf) +#define CTL_END (KEY_OFFSET + 0xc0) + +#define KEY_A1 (KEY_OFFSET + 0xc1) /* upper left on Virtual keypad */ +#define KEY_A2 (KEY_OFFSET + 0xc2) /* upper middle on Virt. keypad */ +#define KEY_A3 (KEY_OFFSET + 0xc3) /* upper right on Vir. keypad */ +#define KEY_B1 (KEY_OFFSET + 0xc4) /* middle left on Virt. keypad */ +#define KEY_B2 (KEY_OFFSET + 0xc5) /* center on Virt. keypad */ +#define KEY_B3 (KEY_OFFSET + 0xc6) /* middle right on Vir. keypad */ +#define KEY_C1 (KEY_OFFSET + 0xc7) /* lower left on Virt. keypad */ +#define KEY_C2 (KEY_OFFSET + 0xc8) /* lower middle on Virt. keypad */ +#define KEY_C3 (KEY_OFFSET + 0xc9) /* lower right on Vir. keypad */ + +#define PADSLASH (KEY_OFFSET + 0xca) /* slash on keypad */ +#define PADENTER (KEY_OFFSET + 0xcb) /* enter on keypad */ +#define CTL_PADENTER (KEY_OFFSET + 0xcc) /* ctl-enter on keypad */ +#define ALT_PADENTER (KEY_OFFSET + 0xcd) /* alt-enter on keypad */ +#define PADSTOP (KEY_OFFSET + 0xce) /* stop on keypad */ +#define PADSTAR (KEY_OFFSET + 0xcf) /* star on keypad */ +#define PADMINUS (KEY_OFFSET + 0xd0) /* minus on keypad */ +#define PADPLUS (KEY_OFFSET + 0xd1) /* plus on keypad */ +#define CTL_PADSTOP (KEY_OFFSET + 0xd2) /* ctl-stop on keypad */ +#define CTL_PADCENTER (KEY_OFFSET + 0xd3) /* ctl-enter on keypad */ +#define CTL_PADPLUS (KEY_OFFSET + 0xd4) /* ctl-plus on keypad */ +#define CTL_PADMINUS (KEY_OFFSET + 0xd5) /* ctl-minus on keypad */ +#define CTL_PADSLASH (KEY_OFFSET + 0xd6) /* ctl-slash on keypad */ +#define CTL_PADSTAR (KEY_OFFSET + 0xd7) /* ctl-star on keypad */ +#define ALT_PADPLUS (KEY_OFFSET + 0xd8) /* alt-plus on keypad */ +#define ALT_PADMINUS (KEY_OFFSET + 0xd9) /* alt-minus on keypad */ +#define ALT_PADSLASH (KEY_OFFSET + 0xda) /* alt-slash on keypad */ +#define ALT_PADSTAR (KEY_OFFSET + 0xdb) /* alt-star on keypad */ +#define ALT_PADSTOP (KEY_OFFSET + 0xdc) /* alt-stop on keypad */ +#define CTL_INS (KEY_OFFSET + 0xdd) /* ctl-insert */ +#define ALT_DEL (KEY_OFFSET + 0xde) /* alt-delete */ +#define ALT_INS (KEY_OFFSET + 0xdf) /* alt-insert */ +#define CTL_UP (KEY_OFFSET + 0xe0) /* ctl-up arrow */ +#define CTL_DOWN (KEY_OFFSET + 0xe1) /* ctl-down arrow */ +#define CTL_TAB (KEY_OFFSET + 0xe2) /* ctl-tab */ +#define ALT_TAB (KEY_OFFSET + 0xe3) +#define ALT_MINUS (KEY_OFFSET + 0xe4) +#define ALT_EQUAL (KEY_OFFSET + 0xe5) +#define ALT_HOME (KEY_OFFSET + 0xe6) +#define ALT_PGUP (KEY_OFFSET + 0xe7) +#define ALT_PGDN (KEY_OFFSET + 0xe8) +#define ALT_END (KEY_OFFSET + 0xe9) +#define ALT_UP (KEY_OFFSET + 0xea) /* alt-up arrow */ +#define ALT_DOWN (KEY_OFFSET + 0xeb) /* alt-down arrow */ +#define ALT_RIGHT (KEY_OFFSET + 0xec) /* alt-right arrow */ +#define ALT_LEFT (KEY_OFFSET + 0xed) /* alt-left arrow */ +#define ALT_ENTER (KEY_OFFSET + 0xee) /* alt-enter */ +#define ALT_ESC (KEY_OFFSET + 0xef) /* alt-escape */ +#define ALT_BQUOTE (KEY_OFFSET + 0xf0) /* alt-back quote */ +#define ALT_LBRACKET (KEY_OFFSET + 0xf1) /* alt-left bracket */ +#define ALT_RBRACKET (KEY_OFFSET + 0xf2) /* alt-right bracket */ +#define ALT_SEMICOLON (KEY_OFFSET + 0xf3) /* alt-semi-colon */ +#define ALT_FQUOTE (KEY_OFFSET + 0xf4) /* alt-forward quote */ +#define ALT_COMMA (KEY_OFFSET + 0xf5) /* alt-comma */ +#define ALT_STOP (KEY_OFFSET + 0xf6) /* alt-stop */ +#define ALT_FSLASH (KEY_OFFSET + 0xf7) /* alt-forward slash */ +#define ALT_BKSP (KEY_OFFSET + 0xf8) /* alt-backspace */ +#define CTL_BKSP (KEY_OFFSET + 0xf9) /* ctl-backspace */ +#define PAD0 (KEY_OFFSET + 0xfa) /* keypad 0 */ + +#define CTL_PAD0 (KEY_OFFSET + 0xfb) /* ctl-keypad 0 */ +#define CTL_PAD1 (KEY_OFFSET + 0xfc) +#define CTL_PAD2 (KEY_OFFSET + 0xfd) +#define CTL_PAD3 (KEY_OFFSET + 0xfe) +#define CTL_PAD4 (KEY_OFFSET + 0xff) +#define CTL_PAD5 (KEY_OFFSET + 0x100) +#define CTL_PAD6 (KEY_OFFSET + 0x101) +#define CTL_PAD7 (KEY_OFFSET + 0x102) +#define CTL_PAD8 (KEY_OFFSET + 0x103) +#define CTL_PAD9 (KEY_OFFSET + 0x104) + +#define ALT_PAD0 (KEY_OFFSET + 0x105) /* alt-keypad 0 */ +#define ALT_PAD1 (KEY_OFFSET + 0x106) +#define ALT_PAD2 (KEY_OFFSET + 0x107) +#define ALT_PAD3 (KEY_OFFSET + 0x108) +#define ALT_PAD4 (KEY_OFFSET + 0x109) +#define ALT_PAD5 (KEY_OFFSET + 0x10a) +#define ALT_PAD6 (KEY_OFFSET + 0x10b) +#define ALT_PAD7 (KEY_OFFSET + 0x10c) +#define ALT_PAD8 (KEY_OFFSET + 0x10d) +#define ALT_PAD9 (KEY_OFFSET + 0x10e) + +#define CTL_DEL (KEY_OFFSET + 0x10f) /* clt-delete */ +#define ALT_BSLASH (KEY_OFFSET + 0x110) /* alt-back slash */ +#define CTL_ENTER (KEY_OFFSET + 0x111) /* ctl-enter */ + +#define SHF_PADENTER (KEY_OFFSET + 0x112) /* shift-enter on keypad */ +#define SHF_PADSLASH (KEY_OFFSET + 0x113) /* shift-slash on keypad */ +#define SHF_PADSTAR (KEY_OFFSET + 0x114) /* shift-star on keypad */ +#define SHF_PADPLUS (KEY_OFFSET + 0x115) /* shift-plus on keypad */ +#define SHF_PADMINUS (KEY_OFFSET + 0x116) /* shift-minus on keypad */ +#define SHF_UP (KEY_OFFSET + 0x117) /* shift-up on keypad */ +#define SHF_DOWN (KEY_OFFSET + 0x118) /* shift-down on keypad */ +#define SHF_IC (KEY_OFFSET + 0x119) /* shift-insert on keypad */ +#define SHF_DC (KEY_OFFSET + 0x11a) /* shift-delete on keypad */ + +#define KEY_MOUSE (KEY_OFFSET + 0x11b) /* "mouse" key */ +#define KEY_SHIFT_L (KEY_OFFSET + 0x11c) /* Left-shift */ +#define KEY_SHIFT_R (KEY_OFFSET + 0x11d) /* Right-shift */ +#define KEY_CONTROL_L (KEY_OFFSET + 0x11e) /* Left-control */ +#define KEY_CONTROL_R (KEY_OFFSET + 0x11f) /* Right-control */ +#define KEY_ALT_L (KEY_OFFSET + 0x120) /* Left-alt */ +#define KEY_ALT_R (KEY_OFFSET + 0x121) /* Right-alt */ +#define KEY_RESIZE (KEY_OFFSET + 0x122) /* Window resize */ +#define KEY_SUP (KEY_OFFSET + 0x123) /* Shifted up arrow */ +#define KEY_SDOWN (KEY_OFFSET + 0x124) /* Shifted down arrow */ + + /* The following were added 2011 Sep 14, and are */ + /* not returned by most flavors of PDCurses: */ + +#define CTL_SEMICOLON (KEY_OFFSET + 0x125) +#define CTL_EQUAL (KEY_OFFSET + 0x126) +#define CTL_COMMA (KEY_OFFSET + 0x127) +#define CTL_MINUS (KEY_OFFSET + 0x128) +#define CTL_STOP (KEY_OFFSET + 0x129) +#define CTL_FSLASH (KEY_OFFSET + 0x12a) +#define CTL_BQUOTE (KEY_OFFSET + 0x12b) + +#define KEY_APPS (KEY_OFFSET + 0x12c) +#define KEY_SAPPS (KEY_OFFSET + 0x12d) +#define CTL_APPS (KEY_OFFSET + 0x12e) +#define ALT_APPS (KEY_OFFSET + 0x12f) + +#define KEY_PAUSE (KEY_OFFSET + 0x130) +#define KEY_SPAUSE (KEY_OFFSET + 0x131) +#define CTL_PAUSE (KEY_OFFSET + 0x132) + +#define KEY_PRINTSCREEN (KEY_OFFSET + 0x133) +#define ALT_PRINTSCREEN (KEY_OFFSET + 0x134) +#define KEY_SCROLLLOCK (KEY_OFFSET + 0x135) +#define ALT_SCROLLLOCK (KEY_OFFSET + 0x136) + +#define CTL_0 (KEY_OFFSET + 0x137) +#define CTL_1 (KEY_OFFSET + 0x138) +#define CTL_2 (KEY_OFFSET + 0x139) +#define CTL_3 (KEY_OFFSET + 0x13a) +#define CTL_4 (KEY_OFFSET + 0x13b) +#define CTL_5 (KEY_OFFSET + 0x13c) +#define CTL_6 (KEY_OFFSET + 0x13d) +#define CTL_7 (KEY_OFFSET + 0x13e) +#define CTL_8 (KEY_OFFSET + 0x13f) +#define CTL_9 (KEY_OFFSET + 0x140) + +#define KEY_BROWSER_BACK (KEY_OFFSET + 0x141) +#define KEY_SBROWSER_BACK (KEY_OFFSET + 0x142) +#define KEY_CBROWSER_BACK (KEY_OFFSET + 0x143) +#define KEY_ABROWSER_BACK (KEY_OFFSET + 0x144) +#define KEY_BROWSER_FWD (KEY_OFFSET + 0x145) +#define KEY_SBROWSER_FWD (KEY_OFFSET + 0x146) +#define KEY_CBROWSER_FWD (KEY_OFFSET + 0x147) +#define KEY_ABROWSER_FWD (KEY_OFFSET + 0x148) +#define KEY_BROWSER_REF (KEY_OFFSET + 0x149) +#define KEY_SBROWSER_REF (KEY_OFFSET + 0x14A) +#define KEY_CBROWSER_REF (KEY_OFFSET + 0x14B) +#define KEY_ABROWSER_REF (KEY_OFFSET + 0x14C) +#define KEY_BROWSER_STOP (KEY_OFFSET + 0x14D) +#define KEY_SBROWSER_STOP (KEY_OFFSET + 0x14E) +#define KEY_CBROWSER_STOP (KEY_OFFSET + 0x14F) +#define KEY_ABROWSER_STOP (KEY_OFFSET + 0x150) +#define KEY_SEARCH (KEY_OFFSET + 0x151) +#define KEY_SSEARCH (KEY_OFFSET + 0x152) +#define KEY_CSEARCH (KEY_OFFSET + 0x153) +#define KEY_ASEARCH (KEY_OFFSET + 0x154) +#define KEY_FAVORITES (KEY_OFFSET + 0x155) +#define KEY_SFAVORITES (KEY_OFFSET + 0x156) +#define KEY_CFAVORITES (KEY_OFFSET + 0x157) +#define KEY_AFAVORITES (KEY_OFFSET + 0x158) +#define KEY_BROWSER_HOME (KEY_OFFSET + 0x159) +#define KEY_SBROWSER_HOME (KEY_OFFSET + 0x15A) +#define KEY_CBROWSER_HOME (KEY_OFFSET + 0x15B) +#define KEY_ABROWSER_HOME (KEY_OFFSET + 0x15C) +#define KEY_VOLUME_MUTE (KEY_OFFSET + 0x15D) +#define KEY_SVOLUME_MUTE (KEY_OFFSET + 0x15E) +#define KEY_CVOLUME_MUTE (KEY_OFFSET + 0x15F) +#define KEY_AVOLUME_MUTE (KEY_OFFSET + 0x160) +#define KEY_VOLUME_DOWN (KEY_OFFSET + 0x161) +#define KEY_SVOLUME_DOWN (KEY_OFFSET + 0x162) +#define KEY_CVOLUME_DOWN (KEY_OFFSET + 0x163) +#define KEY_AVOLUME_DOWN (KEY_OFFSET + 0x164) +#define KEY_VOLUME_UP (KEY_OFFSET + 0x165) +#define KEY_SVOLUME_UP (KEY_OFFSET + 0x166) +#define KEY_CVOLUME_UP (KEY_OFFSET + 0x167) +#define KEY_AVOLUME_UP (KEY_OFFSET + 0x168) +#define KEY_NEXT_TRACK (KEY_OFFSET + 0x169) +#define KEY_SNEXT_TRACK (KEY_OFFSET + 0x16A) +#define KEY_CNEXT_TRACK (KEY_OFFSET + 0x16B) +#define KEY_ANEXT_TRACK (KEY_OFFSET + 0x16C) +#define KEY_PREV_TRACK (KEY_OFFSET + 0x16D) +#define KEY_SPREV_TRACK (KEY_OFFSET + 0x16E) +#define KEY_CPREV_TRACK (KEY_OFFSET + 0x16F) +#define KEY_APREV_TRACK (KEY_OFFSET + 0x170) +#define KEY_MEDIA_STOP (KEY_OFFSET + 0x171) +#define KEY_SMEDIA_STOP (KEY_OFFSET + 0x172) +#define KEY_CMEDIA_STOP (KEY_OFFSET + 0x173) +#define KEY_AMEDIA_STOP (KEY_OFFSET + 0x174) +#define KEY_PLAY_PAUSE (KEY_OFFSET + 0x175) +#define KEY_SPLAY_PAUSE (KEY_OFFSET + 0x176) +#define KEY_CPLAY_PAUSE (KEY_OFFSET + 0x177) +#define KEY_APLAY_PAUSE (KEY_OFFSET + 0x178) +#define KEY_LAUNCH_MAIL (KEY_OFFSET + 0x179) +#define KEY_SLAUNCH_MAIL (KEY_OFFSET + 0x17A) +#define KEY_CLAUNCH_MAIL (KEY_OFFSET + 0x17B) +#define KEY_ALAUNCH_MAIL (KEY_OFFSET + 0x17C) +#define KEY_MEDIA_SELECT (KEY_OFFSET + 0x17D) +#define KEY_SMEDIA_SELECT (KEY_OFFSET + 0x17E) +#define KEY_CMEDIA_SELECT (KEY_OFFSET + 0x17F) +#define KEY_AMEDIA_SELECT (KEY_OFFSET + 0x180) +#define KEY_LAUNCH_APP1 (KEY_OFFSET + 0x181) +#define KEY_SLAUNCH_APP1 (KEY_OFFSET + 0x182) +#define KEY_CLAUNCH_APP1 (KEY_OFFSET + 0x183) +#define KEY_ALAUNCH_APP1 (KEY_OFFSET + 0x184) +#define KEY_LAUNCH_APP2 (KEY_OFFSET + 0x185) +#define KEY_SLAUNCH_APP2 (KEY_OFFSET + 0x186) +#define KEY_CLAUNCH_APP2 (KEY_OFFSET + 0x187) +#define KEY_ALAUNCH_APP2 (KEY_OFFSET + 0x188) + +#define KEY_MIN KEY_BREAK /* Minimum curses key value */ +#define KEY_MAX KEY_ALAUNCH_APP2 /* Maximum curses key */ + +#define KEY_F(n) (KEY_F0 + (n)) + +/*---------------------------------------------------------------------- + * + * PDCurses Function Declarations + * + */ + +/* Standard */ + +PDCEX int addch(const chtype); +PDCEX int addchnstr(const chtype *, int); +PDCEX int addchstr(const chtype *); +PDCEX int addnstr(const char *, int); +PDCEX int addstr(const char *); +PDCEX int attroff(chtype); +PDCEX int attron(chtype); +PDCEX int attrset(chtype); +PDCEX int attr_get(attr_t *, short *, void *); +PDCEX int attr_off(attr_t, void *); +PDCEX int attr_on(attr_t, void *); +PDCEX int attr_set(attr_t, short, void *); +PDCEX int baudrate(void); +PDCEX int beep(void); +PDCEX int bkgd(chtype); +PDCEX void bkgdset(chtype); +PDCEX int border(chtype, chtype, chtype, chtype, + chtype, chtype, chtype, chtype); +PDCEX int box(WINDOW *, chtype, chtype); +PDCEX bool can_change_color(void); +PDCEX int cbreak(void); +PDCEX int chgat(int, attr_t, short, const void *); +PDCEX int clearok(WINDOW *, bool); +PDCEX int clear(void); +PDCEX int clrtobot(void); +PDCEX int clrtoeol(void); +PDCEX int color_content(short, short *, short *, short *); +PDCEX int color_set(short, void *); +PDCEX int copywin(const WINDOW *, WINDOW *, int, int, int, + int, int, int, int); +PDCEX int curs_set(int); +PDCEX int def_prog_mode(void); +PDCEX int def_shell_mode(void); +PDCEX int delay_output(int); +PDCEX int delch(void); +PDCEX int deleteln(void); +PDCEX void delscreen(SCREEN *); +PDCEX int delwin(WINDOW *); +PDCEX WINDOW *derwin(WINDOW *, int, int, int, int); +PDCEX int doupdate(void); +PDCEX WINDOW *dupwin(WINDOW *); +PDCEX int echochar(const chtype); +PDCEX int echo(void); +PDCEX int endwin(void); +PDCEX char erasechar(void); +PDCEX int erase(void); +PDCEX void filter(void); +PDCEX int flash(void); +PDCEX int flushinp(void); +PDCEX chtype getbkgd(WINDOW *); +PDCEX int getnstr(char *, int); +PDCEX int getstr(char *); +PDCEX WINDOW *getwin(FILE *); +PDCEX int halfdelay(int); +PDCEX bool has_colors(void); +PDCEX bool has_ic(void); +PDCEX bool has_il(void); +PDCEX int hline(chtype, int); +PDCEX void idcok(WINDOW *, bool); +PDCEX int idlok(WINDOW *, bool); +PDCEX void immedok(WINDOW *, bool); +PDCEX int inchnstr(chtype *, int); +PDCEX int inchstr(chtype *); +PDCEX chtype inch(void); +PDCEX int init_color(short, short, short, short); +PDCEX int init_pair(short, short, short); +PDCEX WINDOW *initscr(void); +PDCEX int innstr(char *, int); +PDCEX int insch(chtype); +PDCEX int insdelln(int); +PDCEX int insertln(void); +PDCEX int insnstr(const char *, int); +PDCEX int insstr(const char *); +PDCEX int instr(char *); +PDCEX int intrflush(WINDOW *, bool); +PDCEX bool isendwin(void); +PDCEX bool is_linetouched(WINDOW *, int); +PDCEX bool is_wintouched(WINDOW *); +PDCEX char *keyname(int); +PDCEX int keypad(WINDOW *, bool); +PDCEX char killchar(void); +PDCEX int leaveok(WINDOW *, bool); +PDCEX char *longname(void); +PDCEX int meta(WINDOW *, bool); +PDCEX int move(int, int); +PDCEX int mvaddch(int, int, const chtype); +PDCEX int mvaddchnstr(int, int, const chtype *, int); +PDCEX int mvaddchstr(int, int, const chtype *); +PDCEX int mvaddnstr(int, int, const char *, int); +PDCEX int mvaddstr(int, int, const char *); +PDCEX int mvchgat(int, int, int, attr_t, short, const void *); +PDCEX int mvcur(int, int, int, int); +PDCEX int mvdelch(int, int); +PDCEX int mvderwin(WINDOW *, int, int); +PDCEX int mvgetch(int, int); +PDCEX int mvgetnstr(int, int, char *, int); +PDCEX int mvgetstr(int, int, char *); +PDCEX int mvhline(int, int, chtype, int); +PDCEX chtype mvinch(int, int); +PDCEX int mvinchnstr(int, int, chtype *, int); +PDCEX int mvinchstr(int, int, chtype *); +PDCEX int mvinnstr(int, int, char *, int); +PDCEX int mvinsch(int, int, chtype); +PDCEX int mvinsnstr(int, int, const char *, int); +PDCEX int mvinsstr(int, int, const char *); +PDCEX int mvinstr(int, int, char *); +PDCEX int mvprintw(int, int, const char *, ...); +PDCEX int mvscanw(int, int, const char *, ...); +PDCEX int mvvline(int, int, chtype, int); +PDCEX int mvwaddchnstr(WINDOW *, int, int, const chtype *, int); +PDCEX int mvwaddchstr(WINDOW *, int, int, const chtype *); +PDCEX int mvwaddch(WINDOW *, int, int, const chtype); +PDCEX int mvwaddnstr(WINDOW *, int, int, const char *, int); +PDCEX int mvwaddstr(WINDOW *, int, int, const char *); +PDCEX int mvwchgat(WINDOW *, int, int, int, attr_t, short, const void *); +PDCEX int mvwdelch(WINDOW *, int, int); +PDCEX int mvwgetch(WINDOW *, int, int); +PDCEX int mvwgetnstr(WINDOW *, int, int, char *, int); +PDCEX int mvwgetstr(WINDOW *, int, int, char *); +PDCEX int mvwhline(WINDOW *, int, int, chtype, int); +PDCEX int mvwinchnstr(WINDOW *, int, int, chtype *, int); +PDCEX int mvwinchstr(WINDOW *, int, int, chtype *); +PDCEX chtype mvwinch(WINDOW *, int, int); +PDCEX int mvwinnstr(WINDOW *, int, int, char *, int); +PDCEX int mvwinsch(WINDOW *, int, int, chtype); +PDCEX int mvwinsnstr(WINDOW *, int, int, const char *, int); +PDCEX int mvwinsstr(WINDOW *, int, int, const char *); +PDCEX int mvwinstr(WINDOW *, int, int, char *); +PDCEX int mvwin(WINDOW *, int, int); +PDCEX int mvwprintw(WINDOW *, int, int, const char *, ...); +PDCEX int mvwscanw(WINDOW *, int, int, const char *, ...); +PDCEX int mvwvline(WINDOW *, int, int, chtype, int); +PDCEX int napms(int); +PDCEX WINDOW *newpad(int, int); +PDCEX SCREEN *newterm(const char *, FILE *, FILE *); +PDCEX WINDOW *newwin(int, int, int, int); +PDCEX int nl(void); +PDCEX int nocbreak(void); +PDCEX int nodelay(WINDOW *, bool); +PDCEX int noecho(void); +PDCEX int nonl(void); +PDCEX void noqiflush(void); +PDCEX int noraw(void); +PDCEX int notimeout(WINDOW *, bool); +PDCEX int overlay(const WINDOW *, WINDOW *); +PDCEX int overwrite(const WINDOW *, WINDOW *); +PDCEX int pair_content(short, short *, short *); +PDCEX int pechochar(WINDOW *, chtype); +PDCEX int pnoutrefresh(WINDOW *, int, int, int, int, int, int); +PDCEX int prefresh(WINDOW *, int, int, int, int, int, int); +PDCEX int printw(const char *, ...); +PDCEX int putwin(WINDOW *, FILE *); +PDCEX void qiflush(void); +PDCEX int raw(void); +PDCEX int redrawwin(WINDOW *); +PDCEX int refresh(void); +PDCEX int reset_prog_mode(void); +PDCEX int reset_shell_mode(void); +PDCEX int resetty(void); +PDCEX int ripoffline(int, int (*)(WINDOW *, int)); +PDCEX int savetty(void); +PDCEX int scanw(const char *, ...); +PDCEX int scr_dump(const char *); +PDCEX int scr_init(const char *); +PDCEX int scr_restore(const char *); +PDCEX int scr_set(const char *); +PDCEX int scrl(int); +PDCEX int scroll(WINDOW *); +PDCEX int scrollok(WINDOW *, bool); +PDCEX SCREEN *set_term(SCREEN *); +PDCEX int setscrreg(int, int); +PDCEX int slk_attroff(const chtype); +PDCEX int slk_attr_off(const attr_t, void *); +PDCEX int slk_attron(const chtype); +PDCEX int slk_attr_on(const attr_t, void *); +PDCEX int slk_attrset(const chtype); +PDCEX int slk_attr_set(const attr_t, short, void *); +PDCEX int slk_clear(void); +PDCEX int slk_color(short); +PDCEX int slk_init(int); +PDCEX char *slk_label(int); +PDCEX int slk_noutrefresh(void); +PDCEX int slk_refresh(void); +PDCEX int slk_restore(void); +PDCEX int slk_set(int, const char *, int); +PDCEX int slk_touch(void); +PDCEX int standend(void); +PDCEX int standout(void); +PDCEX int start_color(void); +PDCEX WINDOW *subpad(WINDOW *, int, int, int, int); +PDCEX WINDOW *subwin(WINDOW *, int, int, int, int); +PDCEX int syncok(WINDOW *, bool); +PDCEX chtype termattrs(void); +PDCEX attr_t term_attrs(void); +PDCEX char *termname(void); +PDCEX void timeout(int); +PDCEX int touchline(WINDOW *, int, int); +PDCEX int touchwin(WINDOW *); +PDCEX int typeahead(int); +PDCEX int untouchwin(WINDOW *); +PDCEX void use_env(bool); +PDCEX int vidattr(chtype); +PDCEX int vid_attr(attr_t, short, void *); +PDCEX int vidputs(chtype, int (*)(int)); +PDCEX int vid_puts(attr_t, short, void *, int (*)(int)); +PDCEX int vline(chtype, int); +PDCEX int vw_printw(WINDOW *, const char *, va_list); +PDCEX int vwprintw(WINDOW *, const char *, va_list); +PDCEX int vw_scanw(WINDOW *, const char *, va_list); +PDCEX int vwscanw(WINDOW *, const char *, va_list); +PDCEX int waddchnstr(WINDOW *, const chtype *, int); +PDCEX int waddchstr(WINDOW *, const chtype *); +PDCEX int waddch(WINDOW *, const chtype); +PDCEX int waddnstr(WINDOW *, const char *, int); +PDCEX int waddstr(WINDOW *, const char *); +PDCEX int wattroff(WINDOW *, chtype); +PDCEX int wattron(WINDOW *, chtype); +PDCEX int wattrset(WINDOW *, chtype); +PDCEX int wattr_get(WINDOW *, attr_t *, short *, void *); +PDCEX int wattr_off(WINDOW *, attr_t, void *); +PDCEX int wattr_on(WINDOW *, attr_t, void *); +PDCEX int wattr_set(WINDOW *, attr_t, short, void *); +PDCEX void wbkgdset(WINDOW *, chtype); +PDCEX int wbkgd(WINDOW *, chtype); +PDCEX int wborder(WINDOW *, chtype, chtype, chtype, chtype, + chtype, chtype, chtype, chtype); +PDCEX int wchgat(WINDOW *, int, attr_t, short, const void *); +PDCEX int wclear(WINDOW *); +PDCEX int wclrtobot(WINDOW *); +PDCEX int wclrtoeol(WINDOW *); +PDCEX int wcolor_set(WINDOW *, short, void *); +PDCEX void wcursyncup(WINDOW *); +PDCEX int wdelch(WINDOW *); +PDCEX int wdeleteln(WINDOW *); +PDCEX int wechochar(WINDOW *, const chtype); +PDCEX int werase(WINDOW *); +PDCEX int wgetch(WINDOW *); +PDCEX int wgetnstr(WINDOW *, char *, int); +PDCEX int wgetstr(WINDOW *, char *); +PDCEX int whline(WINDOW *, chtype, int); +PDCEX int winchnstr(WINDOW *, chtype *, int); +PDCEX int winchstr(WINDOW *, chtype *); +PDCEX chtype winch(WINDOW *); +PDCEX int winnstr(WINDOW *, char *, int); +PDCEX int winsch(WINDOW *, chtype); +PDCEX int winsdelln(WINDOW *, int); +PDCEX int winsertln(WINDOW *); +PDCEX int winsnstr(WINDOW *, const char *, int); +PDCEX int winsstr(WINDOW *, const char *); +PDCEX int winstr(WINDOW *, char *); +PDCEX int wmove(WINDOW *, int, int); +PDCEX int wnoutrefresh(WINDOW *); +PDCEX int wprintw(WINDOW *, const char *, ...); +PDCEX int wredrawln(WINDOW *, int, int); +PDCEX int wrefresh(WINDOW *); +PDCEX int wscanw(WINDOW *, const char *, ...); +PDCEX int wscrl(WINDOW *, int); +PDCEX int wsetscrreg(WINDOW *, int, int); +PDCEX int wstandend(WINDOW *); +PDCEX int wstandout(WINDOW *); +PDCEX void wsyncdown(WINDOW *); +PDCEX void wsyncup(WINDOW *); +PDCEX void wtimeout(WINDOW *, int); +PDCEX int wtouchln(WINDOW *, int, int, int); +PDCEX int wvline(WINDOW *, chtype, int); + +/* Wide-character functions */ + +#ifdef PDC_WIDE +PDCEX int addnwstr(const wchar_t *, int); +PDCEX int addwstr(const wchar_t *); +PDCEX int add_wch(const cchar_t *); +PDCEX int add_wchnstr(const cchar_t *, int); +PDCEX int add_wchstr(const cchar_t *); +PDCEX int border_set(const cchar_t *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *); +PDCEX int box_set(WINDOW *, const cchar_t *, const cchar_t *); +PDCEX int echo_wchar(const cchar_t *); +PDCEX int erasewchar(wchar_t *); +PDCEX int getbkgrnd(cchar_t *); +PDCEX int getcchar(const cchar_t *, wchar_t *, attr_t *, short *, void *); +PDCEX int getn_wstr(wint_t *, int); +PDCEX int get_wch(wint_t *); +PDCEX int get_wstr(wint_t *); +PDCEX int hline_set(const cchar_t *, int); +PDCEX int innwstr(wchar_t *, int); +PDCEX int ins_nwstr(const wchar_t *, int); +PDCEX int ins_wch(const cchar_t *); +PDCEX int ins_wstr(const wchar_t *); +PDCEX int inwstr(wchar_t *); +PDCEX int in_wch(cchar_t *); +PDCEX int in_wchnstr(cchar_t *, int); +PDCEX int in_wchstr(cchar_t *); +PDCEX char *key_name(wchar_t); +PDCEX int killwchar(wchar_t *); +PDCEX int mvaddnwstr(int, int, const wchar_t *, int); +PDCEX int mvaddwstr(int, int, const wchar_t *); +PDCEX int mvadd_wch(int, int, const cchar_t *); +PDCEX int mvadd_wchnstr(int, int, const cchar_t *, int); +PDCEX int mvadd_wchstr(int, int, const cchar_t *); +PDCEX int mvgetn_wstr(int, int, wint_t *, int); +PDCEX int mvget_wch(int, int, wint_t *); +PDCEX int mvget_wstr(int, int, wint_t *); +PDCEX int mvhline_set(int, int, const cchar_t *, int); +PDCEX int mvinnwstr(int, int, wchar_t *, int); +PDCEX int mvins_nwstr(int, int, const wchar_t *, int); +PDCEX int mvins_wch(int, int, const cchar_t *); +PDCEX int mvins_wstr(int, int, const wchar_t *); +PDCEX int mvinwstr(int, int, wchar_t *); +PDCEX int mvin_wch(int, int, cchar_t *); +PDCEX int mvin_wchnstr(int, int, cchar_t *, int); +PDCEX int mvin_wchstr(int, int, cchar_t *); +PDCEX int mvvline_set(int, int, const cchar_t *, int); +PDCEX int mvwaddnwstr(WINDOW *, int, int, const wchar_t *, int); +PDCEX int mvwaddwstr(WINDOW *, int, int, const wchar_t *); +PDCEX int mvwadd_wch(WINDOW *, int, int, const cchar_t *); +PDCEX int mvwadd_wchnstr(WINDOW *, int, int, const cchar_t *, int); +PDCEX int mvwadd_wchstr(WINDOW *, int, int, const cchar_t *); +PDCEX int mvwgetn_wstr(WINDOW *, int, int, wint_t *, int); +PDCEX int mvwget_wch(WINDOW *, int, int, wint_t *); +PDCEX int mvwget_wstr(WINDOW *, int, int, wint_t *); +PDCEX int mvwhline_set(WINDOW *, int, int, const cchar_t *, int); +PDCEX int mvwinnwstr(WINDOW *, int, int, wchar_t *, int); +PDCEX int mvwins_nwstr(WINDOW *, int, int, const wchar_t *, int); +PDCEX int mvwins_wch(WINDOW *, int, int, const cchar_t *); +PDCEX int mvwins_wstr(WINDOW *, int, int, const wchar_t *); +PDCEX int mvwin_wch(WINDOW *, int, int, cchar_t *); +PDCEX int mvwin_wchnstr(WINDOW *, int, int, cchar_t *, int); +PDCEX int mvwin_wchstr(WINDOW *, int, int, cchar_t *); +PDCEX int mvwinwstr(WINDOW *, int, int, wchar_t *); +PDCEX int mvwvline_set(WINDOW *, int, int, const cchar_t *, int); +PDCEX int pecho_wchar(WINDOW *, const cchar_t*); +PDCEX int setcchar(cchar_t*, const wchar_t*, const attr_t, + short, const void*); +PDCEX int slk_wset(int, const wchar_t *, int); +PDCEX int unget_wch(const wchar_t); +PDCEX int vline_set(const cchar_t *, int); +PDCEX int waddnwstr(WINDOW *, const wchar_t *, int); +PDCEX int waddwstr(WINDOW *, const wchar_t *); +PDCEX int wadd_wch(WINDOW *, const cchar_t *); +PDCEX int wadd_wchnstr(WINDOW *, const cchar_t *, int); +PDCEX int wadd_wchstr(WINDOW *, const cchar_t *); +PDCEX int wbkgrnd(WINDOW *, const cchar_t *); +PDCEX void wbkgrndset(WINDOW *, const cchar_t *); +PDCEX int wborder_set(WINDOW *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *, const cchar_t *, + const cchar_t *, const cchar_t *, const cchar_t *); +PDCEX int wecho_wchar(WINDOW *, const cchar_t *); +PDCEX int wgetbkgrnd(WINDOW *, cchar_t *); +PDCEX int wgetn_wstr(WINDOW *, wint_t *, int); +PDCEX int wget_wch(WINDOW *, wint_t *); +PDCEX int wget_wstr(WINDOW *, wint_t *); +PDCEX int whline_set(WINDOW *, const cchar_t *, int); +PDCEX int winnwstr(WINDOW *, wchar_t *, int); +PDCEX int wins_nwstr(WINDOW *, const wchar_t *, int); +PDCEX int wins_wch(WINDOW *, const cchar_t *); +PDCEX int wins_wstr(WINDOW *, const wchar_t *); +PDCEX int winwstr(WINDOW *, wchar_t *); +PDCEX int win_wch(WINDOW *, cchar_t *); +PDCEX int win_wchnstr(WINDOW *, cchar_t *, int); +PDCEX int win_wchstr(WINDOW *, cchar_t *); +PDCEX wchar_t *wunctrl(cchar_t *); +PDCEX int wvline_set(WINDOW *, const cchar_t *, int); +#endif + +/* Quasi-standard */ + +PDCEX chtype getattrs(WINDOW *); +PDCEX int getbegx(WINDOW *); +PDCEX int getbegy(WINDOW *); +PDCEX int getmaxx(WINDOW *); +PDCEX int getmaxy(WINDOW *); +PDCEX int getparx(WINDOW *); +PDCEX int getpary(WINDOW *); +PDCEX int getcurx(WINDOW *); +PDCEX int getcury(WINDOW *); +PDCEX void traceoff(void); +PDCEX void traceon(void); +PDCEX char *unctrl(chtype); + +PDCEX int crmode(void); +PDCEX int nocrmode(void); +PDCEX int draino(int); +PDCEX int resetterm(void); +PDCEX int fixterm(void); +PDCEX int saveterm(void); +PDCEX void setsyx(int, int); + +PDCEX int mouse_set(unsigned long); +PDCEX int mouse_on(unsigned long); +PDCEX int mouse_off(unsigned long); +PDCEX int request_mouse_pos(void); +PDCEX int map_button(unsigned long); +PDCEX void wmouse_position(WINDOW *, int *, int *); +PDCEX unsigned long getmouse(void); +PDCEX unsigned long getbmap(void); + +/* ncurses */ + +PDCEX int assume_default_colors(int, int); +PDCEX const char *curses_version(void); +PDCEX bool has_key(int); +PDCEX int use_default_colors(void); +PDCEX int wresize(WINDOW *, int, int); + +PDCEX int mouseinterval(int); +PDCEX mmask_t mousemask(mmask_t, mmask_t *); +PDCEX bool mouse_trafo(int *, int *, bool); +PDCEX int nc_getmouse(MEVENT *); +PDCEX int ungetmouse(MEVENT *); +PDCEX bool wenclose(const WINDOW *, int, int); +PDCEX bool wmouse_trafo(const WINDOW *, int *, int *, bool); + +/* PDCurses */ + +PDCEX int addrawch(chtype); +PDCEX int insrawch(chtype); +PDCEX bool is_termresized(void); +PDCEX int mvaddrawch(int, int, chtype); +PDCEX int mvdeleteln(int, int); +PDCEX int mvinsertln(int, int); +PDCEX int mvinsrawch(int, int, chtype); +PDCEX int mvwaddrawch(WINDOW *, int, int, chtype); +PDCEX int mvwdeleteln(WINDOW *, int, int); +PDCEX int mvwinsertln(WINDOW *, int, int); +PDCEX int mvwinsrawch(WINDOW *, int, int, chtype); +PDCEX int raw_output(bool); +PDCEX int resize_term(int, int); +PDCEX WINDOW *resize_window(WINDOW *, int, int); +PDCEX int waddrawch(WINDOW *, chtype); +PDCEX int winsrawch(WINDOW *, chtype); +PDCEX char wordchar(void); + +#ifdef PDC_WIDE +PDCEX wchar_t *slk_wlabel(int); +#endif + +PDCEX void PDC_debug(const char *, ...); +PDCEX int PDC_ungetch(int); +PDCEX int PDC_set_blink(bool); +PDCEX int PDC_set_line_color(short); +PDCEX void PDC_set_title(const char *); + +PDCEX int PDC_clearclipboard(void); +PDCEX int PDC_freeclipboard(char *); +PDCEX int PDC_getclipboard(char **, long *); +PDCEX int PDC_setclipboard(const char *, long); + +PDCEX unsigned long PDC_get_input_fd(void); +PDCEX unsigned long PDC_get_key_modifiers(void); +PDCEX int PDC_return_key_modifiers(bool); +PDCEX int PDC_save_key_modifiers(bool); +PDCEX void PDC_set_resize_limits( const int new_min_lines, + const int new_max_lines, + const int new_min_cols, + const int new_max_cols); + +#define FUNCTION_KEY_SHUT_DOWN 0 +#define FUNCTION_KEY_PASTE 1 +#define FUNCTION_KEY_ENLARGE_FONT 2 +#define FUNCTION_KEY_SHRINK_FONT 3 +#define FUNCTION_KEY_CHOOSE_FONT 4 +#define FUNCTION_KEY_ABORT 5 +#define PDC_MAX_FUNCTION_KEYS 6 + +PDCEX int PDC_set_function_key( const unsigned function, + const int new_key); + +PDCEX WINDOW *Xinitscr(int, char **); +#ifdef XCURSES +PDCEX void XCursesExit(void); +PDCEX int sb_init(void); +PDCEX int sb_set_horz(int, int, int); +PDCEX int sb_set_vert(int, int, int); +PDCEX int sb_get_horz(int *, int *, int *); +PDCEX int sb_get_vert(int *, int *, int *); +PDCEX int sb_refresh(void); + #endif + +/*** Functions defined as macros ***/ + +/* getch() and ungetch() conflict with some DOS libraries */ + +#define getch() wgetch(stdscr) +#define ungetch(ch) PDC_ungetch(ch) + +#define COLOR_PAIR(n) (((chtype)(n) << PDC_COLOR_SHIFT) & A_COLOR) +#define PAIR_NUMBER(n) ((((n) & A_COLOR) >> PDC_COLOR_SHIFT) & 0xff) + +/* These will _only_ work as macros */ + +#define getbegyx(w, y, x) (y = getbegy(w), x = getbegx(w)) +#define getmaxyx(w, y, x) (y = getmaxy(w), x = getmaxx(w)) +#define getparyx(w, y, x) (y = getpary(w), x = getparx(w)) +#define getyx(w, y, x) (y = getcury(w), x = getcurx(w)) + +#define getsyx(y, x) { if (curscr->_leaveit) (y)=(x)=-1; \ + else getyx(curscr,(y),(x)); } + +#ifdef NCURSES_MOUSE_VERSION +# define getmouse(x) nc_getmouse(x) +#endif + +/* return codes from PDC_getclipboard() and PDC_setclipboard() calls */ + +#define PDC_CLIP_SUCCESS 0 +#define PDC_CLIP_ACCESS_ERROR 1 +#define PDC_CLIP_EMPTY 2 +#define PDC_CLIP_MEMORY_ERROR 3 + +/* PDCurses key modifier masks */ + +#define PDC_KEY_MODIFIER_SHIFT 1 +#define PDC_KEY_MODIFIER_CONTROL 2 +#define PDC_KEY_MODIFIER_ALT 4 +#define PDC_KEY_MODIFIER_NUMLOCK 8 +#define PDC_KEY_MODIFIER_REPEAT 16 + +#ifdef __cplusplus +# undef bool +} +#endif + +#endif /* __PDCURSES__ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curspriv.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curspriv.h new file mode 100644 index 000000000..b5edcc173 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/curspriv.h @@ -0,0 +1,134 @@ +/* Public Domain Curses */ + +/* Private definitions and declarations for use within PDCurses. + These should generally not be referenced by applications. */ + +#ifndef __CURSES_INTERNALS__ +#define __CURSES_INTERNALS__ 1 + +#define CURSES_LIBRARY +#include + +#if defined(__TURBOC__) || defined(__EMX__) || defined(__DJGPP__) || \ + defined(__CYGWIN__) || defined(__MINGW32__) || \ + defined(__WATCOMC__) || defined(__PACIFIC__) +# ifndef HAVE_VSSCANF +# define HAVE_VSSCANF /* have vsscanf() */ +# endif +#endif + +#if defined(__CYGWIN__) || defined(__MINGW32__) || \ + defined(__LCC__) || defined(__WATCOMC__) +# ifndef HAVE_VSNPRINTF +# define HAVE_VSNPRINTF /* have vsnprintf() */ +# endif +#endif + +#if defined(_MSC_VER) && defined(_WIN32) && !defined(_CRT_SECURE_NO_DEPRECATE) +# define _CRT_SECURE_NO_DEPRECATE 1 /* kill nonsense warnings */ +#endif + +/*----------------------------------------------------------------------*/ + +typedef struct /* structure for ripped off lines */ +{ + int line; + int (*init)(WINDOW *, int); +} RIPPEDOFFLINE; + +/* Window properties */ + +#define _SUBWIN 0x01 /* window is a subwindow */ +#define _PAD 0x10 /* X/Open Pad. */ +#define _SUBPAD 0x20 /* X/Open subpad. */ + +/* Miscellaneous */ + +#define _NO_CHANGE -1 /* flags line edge unchanged */ + +#define _ECHAR 0x08 /* Erase char (^H) */ +#define _DWCHAR 0x17 /* Delete Word char (^W) */ +#define _DLCHAR 0x15 /* Delete Line char (^U) */ + +extern WINDOW *pdc_lastscr; +extern FILE *pdc_dbfp; /* tracing file pointer (NULL = off) */ +extern bool pdc_color_started; +extern unsigned long pdc_key_modifiers; +extern MOUSE_STATUS pdc_mouse_status; + +/*----------------------------------------------------------------------*/ + +/* Platform implementation functions */ + +void PDC_beep(void); +bool PDC_can_change_color(void); +int PDC_color_content(short, short *, short *, short *); +bool PDC_check_key(void); +int PDC_curs_set(int); +void PDC_flushinp(void); +int PDC_get_columns(void); +int PDC_get_cursor_mode(void); +int PDC_get_key(void); +int PDC_get_rows(void); +void PDC_gotoyx(int, int); +int PDC_init_color(short, short, short, short); +void PDC_init_pair(short, short, short); +int PDC_modifiers_set(void); +int PDC_mouse_set(void); +void PDC_napms(int); +int PDC_pair_content(short, short *, short *); +void PDC_reset_prog_mode(void); +void PDC_reset_shell_mode(void); +int PDC_resize_screen(int, int); +void PDC_restore_screen_mode(int); +void PDC_save_screen_mode(int); +void PDC_scr_close(void); +void PDC_scr_free(void); +int PDC_scr_open(int, char **); +void PDC_set_keyboard_binary(bool); +void PDC_transform_line(int, int, int, const chtype *); +const char *PDC_sysname(void); + +/* Internal cross-module functions */ + +void PDC_init_atrtab(void); +WINDOW *PDC_makelines(WINDOW *); +WINDOW *PDC_makenew(int, int, int, int); +int PDC_mouse_in_slk(int, int); +void PDC_slk_free(void); +void PDC_slk_initialize(void); +void PDC_sync(WINDOW *); + +#ifdef PDC_WIDE +int PDC_mbtowc(wchar_t *, const char *, size_t); +size_t PDC_mbstowcs(wchar_t *, const char *, size_t); +size_t PDC_wcstombs(char *, const wchar_t *, size_t); +#endif + +#ifdef PDCDEBUG +# define PDC_LOG(x) if (pdc_dbfp) PDC_debug x +#else +# define PDC_LOG(x) +#endif + +/* Internal macros for attributes */ + +#ifdef CHTYPE_LONG +# define PDC_COLOR_PAIRS 256 +#else +# define PDC_COLOR_PAIRS 32 +#endif + +#ifndef max +# define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef min +# define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define DIVROUND(num, divisor) (((num) + ((divisor) >> 1)) / (divisor)) + +#define PDC_CLICK_PERIOD 150 /* time to wait for a click, if + not set by mouseinterval() */ + +#endif /* __CURSES_INTERNALS__*/ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/getopt.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/getopt.h new file mode 100644 index 000000000..4de6e853e --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/getopt.h @@ -0,0 +1,93 @@ +/* $Id: getopt.h,v 1.1 2009/10/16 19:50:28 rodney Exp rodney $ */ +/* $OpenBSD: getopt.h,v 1.1 2002/12/03 20:24:29 millert Exp $ */ +/* $NetBSD: getopt.h,v 1.4 2000/07/07 10:43:54 ad Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Dieter Baron and Thomas Klausner. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _GETOPT_H_ +#define _GETOPT_H_ + +#if 0 +#include +#endif + +/* + * GNU-like getopt_long() and 4.4BSD getsubopt()/optreset extensions + */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +struct option { + /* name of long option */ + const char *name; + /* + * one of no_argument, required_argument, and optional_argument: + * whether option takes an argument + */ + int has_arg; + /* if not NULL, set *flag to val when option found */ + int *flag; + /* if flag not NULL, value to set *flag to; else return value */ + int val; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +int getopt_long(int, char * const *, const char *, + const struct option *, int *); +int getopt_long_only(int, char * const *, const char *, + const struct option *, int *); +#ifndef _GETOPT_DEFINED +#define _GETOPT_DEFINED +int getopt(int, char * const *, const char *); +int getsubopt(char **, char * const *, char **); + +extern char *optarg; /* getopt(3) external variables */ +extern int opterr; +extern int optind; +extern int optopt; +extern int optreset; +extern char *suboptarg; /* getsubopt(3) external variable */ +#endif /* _GETOPT_DEFINED */ + +#ifdef __cplusplus +} +#endif +#endif /* !_GETOPT_H_ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/panel.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/panel.h new file mode 100644 index 000000000..7f1fb1f17 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/panel.h @@ -0,0 +1,56 @@ +/* Public Domain Curses */ + +/*----------------------------------------------------------------------* + * Panels for PDCurses * + *----------------------------------------------------------------------*/ + +#ifndef __PDCURSES_PANEL_H__ +#define __PDCURSES_PANEL_H__ 1 + +#include + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +extern "C" +{ +#endif + +typedef struct panelobs +{ + struct panelobs *above; + struct panel *pan; +} PANELOBS; + +typedef struct panel +{ + WINDOW *win; + int wstarty; + int wendy; + int wstartx; + int wendx; + struct panel *below; + struct panel *above; + const void *user; + struct panelobs *obscure; +} PANEL; + +PDCEX int bottom_panel(PANEL *pan); +PDCEX int del_panel(PANEL *pan); +PDCEX int hide_panel(PANEL *pan); +PDCEX int move_panel(PANEL *pan, int starty, int startx); +PDCEX PANEL *new_panel(WINDOW *win); +PDCEX PANEL *panel_above(const PANEL *pan); +PDCEX PANEL *panel_below(const PANEL *pan); +PDCEX int panel_hidden(const PANEL *pan); +PDCEX const void *panel_userptr(const PANEL *pan); +PDCEX WINDOW *panel_window(const PANEL *pan); +PDCEX int replace_panel(PANEL *pan, WINDOW *win); +PDCEX int set_panel_userptr(PANEL *pan, const void *uptr); +PDCEX int show_panel(PANEL *pan); +PDCEX int top_panel(PANEL *pan); +PDCEX void update_panels(void); + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +} +#endif + +#endif /* __PDCURSES_PANEL_H__ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/term.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/term.h new file mode 100644 index 000000000..0ba0b7a7f --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/term.h @@ -0,0 +1,48 @@ +/* Public Domain Curses */ + +/* PDCurses doesn't operate with terminfo, but we need these functions for + compatibility, to allow some things (notably, interface libraries for + other languages) to be compiled. Anyone who tries to actually _use_ + them will be disappointed, since they only return ERR. */ + +#ifndef __PDCURSES_TERM_H__ +#define __PDCURSES_TERM_H__ 1 + +#include + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +extern "C" +{ +#endif + +typedef struct +{ + const char *_termname; +} TERMINAL; + +/* PDCEX is defined in curses.h */ +PDCEX TERMINAL *cur_term; + +int del_curterm(TERMINAL *); +int putp(const char *); +int restartterm(const char *, int, int *); +TERMINAL *set_curterm(TERMINAL *); +int setterm(const char *); +int setupterm(const char *, int, int *); +int tgetent(char *, const char *); +int tgetflag(const char *); +int tgetnum(const char *); +char *tgetstr(const char *, char **); +char *tgoto(const char *, int, int); +int tigetflag(const char *); +int tigetnum(const char *); +char *tigetstr(const char *); +char *tparm(const char *, long, long, long, long, long, + long, long, long, long); +int tputs(const char *, int, int (*)(int)); + +#if defined(__cplusplus) || defined(__cplusplus__) || defined(__CPLUSPLUS) +} +#endif + +#endif /* __PDCURSES_TERM_H__ */ diff --git a/src/cc/rogue/x86_64-w64-msvc/deps/install/include/unistd.h b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/unistd.h new file mode 100644 index 000000000..5d2440309 --- /dev/null +++ b/src/cc/rogue/x86_64-w64-msvc/deps/install/include/unistd.h @@ -0,0 +1,56 @@ +#ifndef _UNISTD_H +#define _UNISTD_H 1 + +/* This is intended as a drop-in replacement for unistd.h on Windows. + * Please add functionality as neeeded. + * https://stackoverflow.com/a/826027/1202830 + */ + +#include +#include +#include /* getopt at: https://gist.github.com/ashelly/7776712 */ +#include /* for getpid() and the exec..() family */ +#include /* for _getcwd() and _chdir() */ + +#define srandom srand +#define random rand + +/* Values for the second argument to access. + These may be OR'd together. */ +#define R_OK 4 /* Test for read permission. */ +#define W_OK 2 /* Test for write permission. */ +//#define X_OK 1 /* execute permission - unsupported in windows*/ +#define F_OK 0 /* Test for existence. */ + +#define access _access +#define dup2 _dup2 +#define execve _execve +#define ftruncate _chsize +#define unlink _unlink +#define fileno _fileno +#define getcwd _getcwd +#define chdir _chdir +#define isatty _isatty +#define lseek _lseek +/* read, write, and close are NOT being #defined here, because while there are file handle specific versions for Windows, they probably don't work for sockets. You need to look at your app and consider whether to call e.g. closesocket(). */ + +#ifdef _WIN64 +#define ssize_t __int64 +#else +#define ssize_t long +#endif + +#define STDIN_FILENO 0 +#define STDOUT_FILENO 1 +#define STDERR_FILENO 2 +/* should be in some equivalent to */ +typedef __int8 int8_t; +typedef __int16 int16_t; +typedef __int32 int32_t; +typedef __int64 int64_t; +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#endif /* unistd.h */ \ No newline at end of file diff --git a/src/cc/rogue/xcrypt.c b/src/cc/rogue/xcrypt.c new file mode 100644 index 000000000..671e20718 --- /dev/null +++ b/src/cc/rogue/xcrypt.c @@ -0,0 +1,707 @@ +/* + * FreeSec: libcrypt + * + * Copyright (C) 1994 David Burren + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name(s) of the author(s) nor the names of other contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * This is an original implementation of the DES and the crypt(3) interfaces + * by David Burren . + * + * An excellent reference on the underlying algorithm (and related + * algorithms) is: + * + * B. Schneier, Applied Cryptography: protocols, algorithms, + * and source code in C, John Wiley & Sons, 1994. + * + * Note that in that book's description of DES the lookups for the initial, + * pbox, and final permutations are inverted (this has been brought to the + * attention of the author). A list of errata for this book has been + * posted to the sci.crypt newsgroup by the author and is available for FTP. + * + * NOTE: + * This file has a static version of des_setkey() so that crypt.o exports + * only the crypt() interface. This is required to make binaries linked + * against crypt.o exportable or re-exportable from the USA. + */ + +#include +#include + +unsigned int md_endian = 0x01020304; + +unsigned int +xntohl(unsigned int x) +{ + if ( *((char *)&md_endian) == 0x01 ) + return(x); + else + return( ((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24) ); +} + +unsigned +xhtonl(unsigned int x) +{ + if ( *((char *)&md_endian) == 0x01 ) + return(x); + else + return( ((x & 0x000000ffU) << 24) | + ((x & 0x0000ff00U) << 8) | + ((x & 0x00ff0000U) >> 8) | + ((x & 0xff000000U) >> 24) ); +} + +#define _PASSWORD_EFMT1 '_' + +static unsigned char IP[64] = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 +}; + +static unsigned char inv_key_perm[64]; +static unsigned char key_perm[56] = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 +}; + +static unsigned char key_shifts[16] = { + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 +}; + +static unsigned char inv_comp_perm[56]; +static unsigned char comp_perm[48] = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 +}; + +/* + * No E box is used, as it's replaced by some ANDs, shifts, and ORs. + */ + +static unsigned char u_sbox[8][64]; +static unsigned char sbox[8][64] = { + { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + }, + { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + }, + { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + }, + { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + }, + { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + }, + { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + }, + { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + }, + { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + } +}; + +static unsigned char un_pbox[32]; +static unsigned char pbox[32] = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 +}; + +static unsigned int bits32[32] = +{ + 0x80000000, 0x40000000, 0x20000000, 0x10000000, + 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, + 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, + 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, + 0x00000008, 0x00000004, 0x00000002, 0x00000001 +}; + +static unsigned char bits8[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + +static unsigned int saltbits; +static int old_salt; +static unsigned int *bits28, *bits24; +static unsigned char init_perm[64], final_perm[64]; +static unsigned int en_keysl[16], en_keysr[16]; +static unsigned int de_keysl[16], de_keysr[16]; +static int des_initialised = 0; +static unsigned char m_sbox[4][4096]; +static unsigned int psbox[4][256]; +static unsigned int ip_maskl[8][256], ip_maskr[8][256]; +static unsigned int fp_maskl[8][256], fp_maskr[8][256]; +static unsigned int key_perm_maskl[8][128], key_perm_maskr[8][128]; +static unsigned int comp_maskl[8][128], comp_maskr[8][128]; +static unsigned int old_rawkey0, old_rawkey1; + +static unsigned char ascii64[] = + "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; +/* 0000000000111111111122222222223333333333444444444455555555556666 */ +/* 0123456789012345678901234567890123456789012345678901234567890123 */ + +static __inline int +ascii_to_bin(char ch) +{ + if (ch > 'z') + return(0); + if (ch >= 'a') + return(ch - 'a' + 38); + if (ch > 'Z') + return(0); + if (ch >= 'A') + return(ch - 'A' + 12); + if (ch > '9') + return(0); + if (ch >= '.') + return(ch - '.'); + return(0); +} + +static void +des_init() +{ + int i, j, b, k, inbit, obit; + unsigned int *p, *il, *ir, *fl, *fr; + + old_rawkey0 = old_rawkey1 = 0; + saltbits = 0; + old_salt = 0; + bits24 = (bits28 = bits32 + 4) + 4; + + /* + * Invert the S-boxes, reordering the input bits. + */ + for (i = 0; i < 8; i++) + for (j = 0; j < 64; j++) { + b = (j & 0x20) | ((j & 1) << 4) | ((j >> 1) & 0xf); + u_sbox[i][j] = sbox[i][b]; + } + + /* + * Convert the inverted S-boxes into 4 arrays of 8 bits. + * Each will handle 12 bits of the S-box input. + */ + for (b = 0; b < 4; b++) + for (i = 0; i < 64; i++) + for (j = 0; j < 64; j++) + m_sbox[b][(i << 6) | j] = + (u_sbox[(b << 1)][i] << 4) | + u_sbox[(b << 1) + 1][j]; + + /* + * Set up the initial & final permutations into a useful form, and + * initialise the inverted key permutation. + */ + for (i = 0; i < 64; i++) { + init_perm[final_perm[i] = IP[i] - 1] = (unsigned char) i; + inv_key_perm[i] = 255; + } + + /* + * Invert the key permutation and initialise the inverted key + * compression permutation. + */ + for (i = 0; i < 56; i++) { + inv_key_perm[key_perm[i] - 1] = (unsigned char) i; + inv_comp_perm[i] = 255; + } + + /* + * Invert the key compression permutation. + */ + for (i = 0; i < 48; i++) { + inv_comp_perm[comp_perm[i] - 1] = (unsigned char) i; + } + + /* + * Set up the OR-mask arrays for the initial and final permutations, + * and for the key initial and compression permutations. + */ + for (k = 0; k < 8; k++) { + for (i = 0; i < 256; i++) { + *(il = &ip_maskl[k][i]) = 0; + *(ir = &ip_maskr[k][i]) = 0; + *(fl = &fp_maskl[k][i]) = 0; + *(fr = &fp_maskr[k][i]) = 0; + for (j = 0; j < 8; j++) { + inbit = 8 * k + j; + if (i & bits8[j]) { + if ((obit = init_perm[inbit]) < 32) + *il |= bits32[obit]; + else + *ir |= bits32[obit-32]; + if ((obit = final_perm[inbit]) < 32) + *fl |= bits32[obit]; + else + *fr |= bits32[obit - 32]; + } + } + } + for (i = 0; i < 128; i++) { + *(il = &key_perm_maskl[k][i]) = 0; + *(ir = &key_perm_maskr[k][i]) = 0; + for (j = 0; j < 7; j++) { + inbit = 8 * k + j; + if (i & bits8[j + 1]) { + if ((obit = inv_key_perm[inbit]) == 255) + continue; + if (obit < 28) + *il |= bits28[obit]; + else + *ir |= bits28[obit - 28]; + } + } + *(il = &comp_maskl[k][i]) = 0; + *(ir = &comp_maskr[k][i]) = 0; + for (j = 0; j < 7; j++) { + inbit = 7 * k + j; + if (i & bits8[j + 1]) { + if ((obit=inv_comp_perm[inbit]) == 255) + continue; + if (obit < 24) + *il |= bits24[obit]; + else + *ir |= bits24[obit - 24]; + } + } + } + } + + /* + * Invert the P-box permutation, and convert into OR-masks for + * handling the output of the S-box arrays setup above. + */ + for (i = 0; i < 32; i++) + un_pbox[pbox[i] - 1] = (unsigned char) i; + + for (b = 0; b < 4; b++) + for (i = 0; i < 256; i++) { + *(p = &psbox[b][i]) = 0; + for (j = 0; j < 8; j++) { + if (i & bits8[j]) + *p |= bits32[un_pbox[8 * b + j]]; + } + } + + des_initialised = 1; +} + +static void +setup_salt(int salt) +{ + unsigned int obit, saltbit; + int i; + + if (salt == old_salt) + return; + old_salt = salt; + + saltbits = 0; + saltbit = 1; + obit = 0x800000; + for (i = 0; i < 24; i++) { + if (salt & saltbit) + saltbits |= obit; + saltbit <<= 1; + obit >>= 1; + } +} + +static int +des_setkey(const char *key) +{ + unsigned int k0, k1, rawkey0, rawkey1; + int shifts, round; + + if (!des_initialised) + des_init(); + + rawkey0 = xntohl(*(unsigned int *) key); + rawkey1 = xntohl(*(unsigned int *) (key + 4)); + + if ((rawkey0 | rawkey1) + && rawkey0 == old_rawkey0 + && rawkey1 == old_rawkey1) { + /* + * Already setup for this key. + * This optimisation fails on a zero key (which is weak and + * has bad parity anyway) in order to simplify the starting + * conditions. + */ + return(0); + } + old_rawkey0 = rawkey0; + old_rawkey1 = rawkey1; + + /* + * Do key permutation and split into two 28-bit subkeys. + */ + k0 = key_perm_maskl[0][rawkey0 >> 25] + | key_perm_maskl[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskl[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskl[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskl[4][rawkey1 >> 25] + | key_perm_maskl[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskl[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskl[7][(rawkey1 >> 1) & 0x7f]; + k1 = key_perm_maskr[0][rawkey0 >> 25] + | key_perm_maskr[1][(rawkey0 >> 17) & 0x7f] + | key_perm_maskr[2][(rawkey0 >> 9) & 0x7f] + | key_perm_maskr[3][(rawkey0 >> 1) & 0x7f] + | key_perm_maskr[4][rawkey1 >> 25] + | key_perm_maskr[5][(rawkey1 >> 17) & 0x7f] + | key_perm_maskr[6][(rawkey1 >> 9) & 0x7f] + | key_perm_maskr[7][(rawkey1 >> 1) & 0x7f]; + /* + * Rotate subkeys and do compression permutation. + */ + shifts = 0; + for (round = 0; round < 16; round++) { + unsigned int t0, t1; + + shifts += key_shifts[round]; + + t0 = (k0 << shifts) | (k0 >> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >> (28 - shifts)); + + de_keysl[15 - round] = + en_keysl[round] = comp_maskl[0][(t0 >> 21) & 0x7f] + | comp_maskl[1][(t0 >> 14) & 0x7f] + | comp_maskl[2][(t0 >> 7) & 0x7f] + | comp_maskl[3][t0 & 0x7f] + | comp_maskl[4][(t1 >> 21) & 0x7f] + | comp_maskl[5][(t1 >> 14) & 0x7f] + | comp_maskl[6][(t1 >> 7) & 0x7f] + | comp_maskl[7][t1 & 0x7f]; + + de_keysr[15 - round] = + en_keysr[round] = comp_maskr[0][(t0 >> 21) & 0x7f] + | comp_maskr[1][(t0 >> 14) & 0x7f] + | comp_maskr[2][(t0 >> 7) & 0x7f] + | comp_maskr[3][t0 & 0x7f] + | comp_maskr[4][(t1 >> 21) & 0x7f] + | comp_maskr[5][(t1 >> 14) & 0x7f] + | comp_maskr[6][(t1 >> 7) & 0x7f] + | comp_maskr[7][t1 & 0x7f]; + } + return(0); +} + +static int +do_des(unsigned int l_in, unsigned int r_in, unsigned int *l_out, + unsigned int *r_out, int count) +{ + /* + * l_in, r_in, l_out, and r_out are in pseudo-"big-endian" format. + */ + unsigned int l, r, *kl, *kr, *kl1, *kr1; + unsigned int f = 0, r48l, r48r; + int round; + + if (count == 0) { + return(1); + } else if (count > 0) { + /* + * Encrypting + */ + kl1 = en_keysl; + kr1 = en_keysr; + } else { + /* + * Decrypting + */ + count = -count; + kl1 = de_keysl; + kr1 = de_keysr; + } + + /* + * Do initial permutation (IP). + */ + l = ip_maskl[0][l_in >> 24] + | ip_maskl[1][(l_in >> 16) & 0xff] + | ip_maskl[2][(l_in >> 8) & 0xff] + | ip_maskl[3][l_in & 0xff] + | ip_maskl[4][r_in >> 24] + | ip_maskl[5][(r_in >> 16) & 0xff] + | ip_maskl[6][(r_in >> 8) & 0xff] + | ip_maskl[7][r_in & 0xff]; + r = ip_maskr[0][l_in >> 24] + | ip_maskr[1][(l_in >> 16) & 0xff] + | ip_maskr[2][(l_in >> 8) & 0xff] + | ip_maskr[3][l_in & 0xff] + | ip_maskr[4][r_in >> 24] + | ip_maskr[5][(r_in >> 16) & 0xff] + | ip_maskr[6][(r_in >> 8) & 0xff] + | ip_maskr[7][r_in & 0xff]; + + while (count--) { + /* + * Do each round. + */ + kl = kl1; + kr = kr1; + round = 16; + while (round--) { + /* + * Expand R to 48 bits (simulate the E-box). + */ + r48l = ((r & 0x00000001) << 23) + | ((r & 0xf8000000) >> 9) + | ((r & 0x1f800000) >> 11) + | ((r & 0x01f80000) >> 13) + | ((r & 0x001f8000) >> 15); + + r48r = ((r & 0x0001f800) << 7) + | ((r & 0x00001f80) << 5) + | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) + | ((r & 0x80000000) >> 31); + /* + * Do salting for crypt() and friends, and + * XOR with the permuted key. + */ + f = (r48l ^ r48r) & saltbits; + r48l ^= f ^ *kl++; + r48r ^= f ^ *kr++; + /* + * Do sbox lookups (which shrink it back to 32 bits) + * and do the pbox permutation at the same time. + */ + f = psbox[0][m_sbox[0][r48l >> 12]] + | psbox[1][m_sbox[1][r48l & 0xfff]] + | psbox[2][m_sbox[2][r48r >> 12]] + | psbox[3][m_sbox[3][r48r & 0xfff]]; + /* + * Now that we've permuted things, complete f(). + */ + f ^= l; + l = r; + r = f; + } + r = l; + l = f; + } + /* + * Do final permutation (inverse of IP). + */ + *l_out = fp_maskl[0][l >> 24] + | fp_maskl[1][(l >> 16) & 0xff] + | fp_maskl[2][(l >> 8) & 0xff] + | fp_maskl[3][l & 0xff] + | fp_maskl[4][r >> 24] + | fp_maskl[5][(r >> 16) & 0xff] + | fp_maskl[6][(r >> 8) & 0xff] + | fp_maskl[7][r & 0xff]; + *r_out = fp_maskr[0][l >> 24] + | fp_maskr[1][(l >> 16) & 0xff] + | fp_maskr[2][(l >> 8) & 0xff] + | fp_maskr[3][l & 0xff] + | fp_maskr[4][r >> 24] + | fp_maskr[5][(r >> 16) & 0xff] + | fp_maskr[6][(r >> 8) & 0xff] + | fp_maskr[7][r & 0xff]; + return(0); +} + +static int +des_cipher(const char *in, char *out, int salt, int count) +{ + unsigned int l_out, r_out, rawl, rawr; + unsigned int x[2]; + int retval; + + if (!des_initialised) + des_init(); + + setup_salt(salt); + + memcpy(x, in, sizeof x); + rawl = xntohl(x[0]); + rawr = xntohl(x[1]); + retval = do_des(rawl, rawr, &l_out, &r_out, count); + + x[0] = xhtonl(l_out); + x[1] = xhtonl(r_out); + memcpy(out, x, sizeof x); + return(retval); +} + +char * +xcrypt( char *key, char *setting) +{ + int i; + unsigned int count, salt, l, r0, r1, keybuf[2]; + unsigned char *p, *q; + static unsigned char output[21]; + + if (!des_initialised) + des_init(); + + /* + * Copy the key, shifting each character up by one bit + * and padding with zeros. + */ + q = (unsigned char *) keybuf; + while ((q - (unsigned char *) keybuf) < sizeof(keybuf)) { + if ((*q++ = *key << 1)) + key++; + } + if (des_setkey((const char *) keybuf)) + return(NULL); + + if (*setting == _PASSWORD_EFMT1) { + /* + * "new"-style: + * setting - underscore, 4 bytes of count, 4 bytes of salt + * key - unlimited characters + */ + for (i = 1, count = 0; i < 5; i++) + count |= ascii_to_bin(setting[i]) << (i - 1) * 6; + + for (i = 5, salt = 0; i < 9; i++) + salt |= ascii_to_bin(setting[i]) << (i - 5) * 6; + + while (*key) { + /* + * Encrypt the key with itself. + */ + if (des_cipher((const char*)keybuf, (char*)keybuf, 0, 1)) + return(NULL); + /* + * And XOR with the next 8 characters of the key. + */ + q = (unsigned char *) keybuf; + while (((q - (unsigned char *) keybuf) < sizeof(keybuf)) && + *key) + *q++ ^= *key++ << 1; + + if (des_setkey((const char *) keybuf)) + return(NULL); + } + strncpy((char *)output, setting, 9); + + /* + * Double check that we weren't given a short setting. + * If we were, the above code will probably have created + * wierd values for count and salt, but we don't really care. + * Just make sure the output string doesn't have an extra + * NUL in it. + */ + output[9] = '\0'; + p = output + strlen((const char *)output); + } else { + /* + * "old"-style: + * setting - 2 bytes of salt + * key - up to 8 characters + */ + count = 25; + + salt = (ascii_to_bin(setting[1]) << 6) + | ascii_to_bin(setting[0]); + + output[0] = setting[0]; + /* + * If the encrypted password that the salt was extracted from + * is only 1 character long, the salt will be corrupted. We + * need to ensure that the output string doesn't have an extra + * NUL in it! + */ + output[1] = setting[1] ? setting[1] : output[0]; + + p = output + 2; + } + setup_salt(salt); + /* + * Do it. + */ + if (do_des(0, 0, &r0, &r1, count)) + return(NULL); + /* + * Now encode the result... + */ + l = (r0 >> 8); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = (r0 << 16) | ((r1 >> 16) & 0xffff); + *p++ = ascii64[(l >> 18) & 0x3f]; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + + l = r1 << 2; + *p++ = ascii64[(l >> 12) & 0x3f]; + *p++ = ascii64[(l >> 6) & 0x3f]; + *p++ = ascii64[l & 0x3f]; + *p = 0; + + return((char *)output); +} diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp new file mode 100644 index 000000000..b0ed60ab2 --- /dev/null +++ b/src/cc/rogue_rpc.cpp @@ -0,0 +1,1652 @@ + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include "cJSON.h" +#include "CCinclude.h" + +#define ROGUE_REGISTRATION 5 +#define ROGUE_REGISTRATIONSIZE (100 * 10000) +#define ROGUE_MAXPLAYERS 64 // need to send unused fees back to globalCC address to prevent leeching +#define ROGUE_MAXKEYSTROKESGAP 60 +#define ROGUE_MAXITERATIONS 777 +#define ROGUE_MAXCASHOUT (777 * COIN) + +#include "rogue/rogue_player.h" + +std::string Rogue_pname = ""; + +/* + Roguelander - using highlander competition between rogue players + + anybody can create a game by specifying maxplayers and buyin. Buyin of 0.00 ROGUE is for newbies and a good way to get experience. All players will start in the identical level, but each level after that will be unique to each player. + + There are two different modes that is based on the maxplayers. If maxplayers is one, then it is basically a practice/farming game where there are no time limits. Also, the gold -> ROGUE conversion rate is cut in half. You can start a single player game as soon as the newgame tx is confirmed. + + If maxplayers is more than one, then it goes into highlander mode. There can only be one winner. Additionally, multiplayer mode adds a timelimit of ROGUE_MAXKEYSTROKESGAP (60) blocks between keystrokes updates. That is approx. one hour and every level it does an automatic update, so as long as you are actively playing, it wont be a problem. The is ROGUE_REGISTRATION blocks waiting period before any of the players can start. The random seed for the game is based on the future blockhash so that way, nobody is able to start before the others. + + rogue is not an easy game to win, so it could be that the winner for a specific group is simply the last one standing. If you bailout of a game you cant win, but you can convert all the ingame gold you gathered at 0.001 ROGUE each. [In the event that there arent enough globally locked funds to payout the ROGUE, you will have to wait until there is. Since 10% of all economic activity creates more globally locked funds, it is just a matter of time before there will be enough funds to payout. Also, you can help speed this up by encouraging others to transact in ROGUE] + + Of course, the most direct way to win, is to get the amulet and come back out of the dungeon. The winner will get all of the buyins from all the players in addition to 0.01 ROGUE for every ingame gold. + + The above alone is enough to make the timeless classic solitaire game into a captivating multiplayer game, where real coins are at stake. However, there is an even better aspect to ROGUE! Whenever your player survives a game, even when you bailout, the entire player and his pack is preserved on the blockchain and becomes a nonfungible asset that you can trade for ROGUE. + + Additionally, instead of just being a collectors item with unique characteristics, the rogue playerdata can be used in any ROGUE game. Just specify the txid that created your character when you register and your character is restored. The better your characteristics your playerdata has, the more likely you are to win in multiplayer mode to win all the buyins. So there is definite economic value to a strong playerdata. + + You can farm characters in single player mode to earn ROGUE or to make competitive playerdata sets. You can purchase existing playerdata for collecting, or for using in battle. + + Here is how to play: + + ./komodo-cli -ac_name=ROGUE cclib newgame 17 \"[3,10]\" -> this will create a hex transaction that when broadcast with sendrawtransaction will get a gametxid onto the blockchain. This specific command was for 3 players and a buyin of 10 ROGUE. Lets assume the gametxid is 4fd6f5cad0fac455e5989ca6eef111b00292845447075a802e9335879146ad5a, most all the other commands will need the gametxid. + + you can always find all the existing games with: + + ./komodo-cli -ac_name=ROGUE cclib pending 17 + + and info about a specific game with: + + ./komodo-cli -ac_name=ROGUE cclib gameinfo 17 \"[%224fd6f5cad0fac455e5989ca6eef111b00292845447075a802e9335879146ad5a%22]\" + + due to quirks of various parsing at the shell, rpc and internal level, the above convention is used where %22 is added where " should be. also all fields are separated by , without any space. + + When you do a gameinfo command it will show a "run" field and that will tell you if you are registered for the game or not. If not, the "run" field shows the register syntax you need to do, if you are registered, it will show the command line to start the rogue game that is playing the registered game. + +./komodo-cli -ac_name=ROGUE cclib register 17 \"[%224fd6f5cad0fac455e5989ca6eef111b00292845447075a802e9335879146ad5a%22,%22playerdata_txid%22]\" + + If you want to cash in your ingame gold and preserve your character for another battle, do the bailout: + +./komodo-cli -ac_name=ROGUE cclib bailout 17 \"[%224fd6f5cad0fac455e5989ca6eef111b00292845447075a802e9335879146ad5a%22]\" + + If you won your game before anybody else did or if you are the last one left who didnt bailout, you can claim the prize: + + ./komodo-cli -ac_name=ROGUE cclib highlander 17 \"[%224fd6f5cad0fac455e5989ca6eef111b00292845447075a802e9335879146ad5a%22]\" + + The txid you get from the bailout or highlander transactions is the "playerdata_txid" that you can use in future games. + + + Transaction details + creategame + vout0 -> txfee highlander vout TCBOO creation + vout1 to vout.maxplayers+1 -> 1of2 registration ROGUE_REGISTRATIONSIZE batons + vout2+maxplayers to vout.2*maxplayers+1 -> 1of2 registration txfee batons for game completion + + register + vin0 -> ROGUE_REGISTRATIONSIZE 1of2 registration baton from creategame + vin1 -> optional nonfungible character vout @ + vin2 -> original creation TCBOO playerdata used + vin3+ -> buyin + vout0 -> keystrokes/completion baton + + keystrokes + vin0 -> txfee 1of2 baton from registration or previous keystrokes + opret -> user input chars + + bailout: must be within ROGUE_MAXKEYSTROKESGAP blocks of last keystrokes + vin0 -> keystrokes baton of completed game with Q + vout0 -> 1% ingame gold + + highlander + vin0 -> txfee highlander vout from creategame TCBOO creation + vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than ROGUE_MAXKEYSTROKESGAP blocks since last keystrokes, it is forfeit + vins -> rest of unspent registration utxo so all newgame vouts are spent + vout0 -> nonfungible character with pack @ + vout1 -> 1% ingame gold and all the buyins + + + then to register you need to spend one of the vouts and also provide the buyin + once you register the gui mode is making automatic keystrokes tx with the raw chars in opreturn. + if during the registration, you provide a character as an input, your gameplay starts with that character instead of the default + + each keystrokes tx spends a baton vout that you had in your register tx + + so from the creategame tx, you can trace the maxplayers vouts to find all the registrations and all the keystrokes to get the keyboard events + + If you quit out of the game, then the in game gold that you earned can be converted to ROGUE coins, but unless you are the last one remaining, any character you input, is permanently spent + + so you can win a multiplayer by being the last player to quit or the first one to win. In either case, you would need to spend a special highlander vout in the original creategame tx. having this as an input allows to create a tx that has the character as the nonfungible token, collect all the buyin and of course the ingame gold + + once you have a non-fungible token, ownership of it can be transferred or traded or spent in a game + */ + +// todo: +// add some more conditions to multiplayer +// how does it work with playertxid instead of pubkey +// keystrokes retry + +//////////////////////// start of CClib interface +//./komodod -ac_name=ROGUE -ac_supply=1000000 -pubkey=03951a6f7967ad784453116bc55cd30c54f91ea8a5b1e9b04d6b29cfd6b395ba6c -addnode=5.9.102.210 -ac_cclib=rogue -ac_perc=10000000 -ac_reward=100000000 -ac_cc=60001 -ac_script=2ea22c80203d1579313abe7d8ea85f48c65ea66fc512c878c0d0e6f6d54036669de940febf8103120c008203000401cc > /dev/null & + +// cclib newgame 17 \"[3,10]\" +// cclib pending 17 +// ./c cclib gameinfo 17 \"[%226d3243c6e5ab383898b28a87e01f6c00b5bdd9687020f17f5caacc8a61febd19%22]\" +// cclib register 17 \"[%22aa81321d8889f881fe3d7c68c905b7447d9143832b0abbef6c2cab49dff8b0cc%22]\" +// ./rogue gui -> creates keystroke files +// ./c cclib register 17 \"[%226d3243c6e5ab383898b28a87e01f6c00b5bdd9687020f17f5caacc8a61febd19%22,%222475182f9d5169d8a3249d17640e4eccd90f4ee43ab04791129b0fa3f177b14a%22]\" +// ./c cclib bailout 17 \"[%226d3243c6e5ab383898b28a87e01f6c00b5bdd9687020f17f5caacc8a61febd19%22]\" +// ./komodo-cli -ac_name=ROGUE cclib register 17 \"[%22a898f4ceef7647ba113b9f3c24ef045f5d134935a3b09bdd1a997b9d474f4c1b%22,%22f11d0cb4e2e4c21f029a1146f8e5926f11456885b7ab7d665096f5efedec8ea0%22]\" + + +CScript rogue_newgameopret(int64_t buyin,int32_t maxplayers) +{ + CScript opret; uint8_t evalcode = EVAL_ROGUE; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'G' << buyin << maxplayers); + return(opret); +} + +CScript rogue_registeropret(uint256 gametxid,uint256 playertxid) +{ + CScript opret; uint8_t evalcode = EVAL_ROGUE; + //fprintf(stderr,"opret.(%s %s).R\n",gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << gametxid << playertxid); + return(opret); +} + +CScript rogue_keystrokesopret(uint256 gametxid,uint256 batontxid,CPubKey pk,std::vectorkeystrokes) +{ + CScript opret; uint8_t evalcode = EVAL_ROGUE; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'K' << gametxid << batontxid << pk << keystrokes); + return(opret); +} + +CScript rogue_highlanderopret(uint8_t funcid,uint256 gametxid,int32_t regslot,CPubKey pk,std::vectorplayerdata,std::string pname) +{ + CScript opret; uint8_t evalcode = EVAL_ROGUE; std::string symbol(ASSETCHAINS_SYMBOL); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << gametxid << symbol << pname << regslot << pk << playerdata ); + return(opret); +} + +uint8_t rogue_highlanderopretdecode(uint256 &gametxid, uint256 &tokenid, int32_t ®slot, CPubKey &pk, std::vector &playerdata, std::string &symbol, std::string &pname,CScript scriptPubKey) +{ + std::string name, description; std::vector vorigPubkey; + std::vector> oprets, opretsDummy; + std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + uint8_t e, f,*script; std::vector voutPubkeys; + tokenid = zeroid; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description, oprets)) == 'c' ) + { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + vopret = vopretNonfungible; + } + else if ( script[1] != 'H' && script[1] != 'Q' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, opretsDummy)) != 0 ) + { + //fprintf(stderr,"decode opret %c tokenid.%s\n",script[1],tokenid.GetHex().c_str()); + GetNonfungibleData(tokenid, vopretNonfungible); //load nonfungible data from the 'tokenbase' tx + vopret = vopretNonfungible; + } + if ( vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> gametxid; ss >> symbol; ss >> pname; ss >> regslot; ss >> pk; ss >> playerdata) != 0 && e == EVAL_ROGUE && (f == 'H' || f == 'Q') ) + { + return(f); + } + fprintf(stderr,"SKIP obsolete: e.%d f.%c game.%s regslot.%d psize.%d\n",e,f,gametxid.GetHex().c_str(),regslot,(int32_t)playerdata.size()); + return(0); +} + +uint8_t rogue_keystrokesopretdecode(uint256 &gametxid,uint256 &batontxid,CPubKey &pk,std::vector &keystrokes,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> batontxid; ss >> pk; ss >> keystrokes) != 0 && e == EVAL_ROGUE && f == 'K' ) + { + return(f); + } + return(0); +} + +uint8_t rogue_registeropretdecode(uint256 &gametxid,uint256 &tokenid,uint256 &playertxid,CScript scriptPubKey) +{ + std::string name, description; std::vector vorigPubkey; + std::vector> oprets; + std::vector vopretNonfungible, vopret, vopretDummy,origpubkey; + uint8_t e, f,*script; std::vector voutPubkeys; + tokenid = zeroid; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( script[1] == 'c' && (f= DecodeTokenCreateOpRet(scriptPubKey,origpubkey,name,description,oprets)) == 'c' ) + { + GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible); + vopret = vopretNonfungible; + } + else if ( script[1] != 'R' && (f= DecodeTokenOpRet(scriptPubKey, e, tokenid, voutPubkeys, oprets)) != 0 ) + { + GetOpretBlob(oprets, OPRETID_ROGUEGAMEDATA, vopretDummy); // blob from non-creation tx opret + vopret = vopretDummy; + } + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> gametxid; ss >> playertxid) != 0 && e == EVAL_ROGUE && f == 'R' ) + { + return(f); + } + //fprintf(stderr,"e.%d f.%c game.%s playertxid.%s\n",e,f,gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + return(0); +} + +uint8_t rogue_newgameopreturndecode(int64_t &buyin,int32_t &maxplayers,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> buyin; ss >> maxplayers) != 0 && e == EVAL_ROGUE && f == 'G' ) + { + return(f); + } + return(0); +} + +void rogue_univalue(UniValue &result,const char *method,int64_t maxplayers,int64_t buyin) +{ + if ( method != 0 ) + { + result.push_back(Pair("name","rogue")); + result.push_back(Pair("method",method)); + } + if ( maxplayers > 0 ) + result.push_back(Pair("maxplayers",maxplayers)); + if ( buyin >= 0 ) + { + result.push_back(Pair("buyin",ValueFromAmount(buyin))); + if ( buyin == 0 ) + result.push_back(Pair("type","newbie")); + else result.push_back(Pair("type","buyin")); + } +} + +int32_t rogue_iamregistered(int32_t maxplayers,uint256 gametxid,CTransaction tx,char *myrogueaddr) +{ + int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; char destaddr[64]; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); + if ( strcmp(myrogueaddr,destaddr) == 0 ) + return(1); + //else fprintf(stderr,"myaddr.%s vs %s\n",myrogueaddr,destaddr); + } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"vout %d is unspent\n",vout); + } + return(0); +} + +int64_t rogue_buyins(uint256 gametxid,int32_t maxplayers) +{ + int32_t i,vout; uint256 spenttxid,hashBlock; CTransaction spenttx; int64_t buyins = 0; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + if ( spenttx.vout[0].nValue > ROGUE_REGISTRATIONSIZE ) + buyins += (spenttx.vout[0].nValue - ROGUE_REGISTRATIONSIZE); + } //else fprintf(stderr,"cant find spenttxid.%s\n",spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"vout %d is unspent\n",vout); + } + return(buyins); +} + +int32_t rogue_isvalidgame(struct CCcontract_info *cp,int32_t &gameheight,CTransaction &tx,int64_t &buyin,int32_t &maxplayers,uint256 txid,int32_t unspentv0) +{ + uint256 hashBlock; int32_t i,numvouts; char coinaddr[64]; CPubKey roguepk; uint64_t txfee = 10000; + buyin = maxplayers = 0; + if ( (txid == zeroid || myGetTransaction(txid,tx,hashBlock) != 0) && (numvouts= tx.vout.size()) > 1 ) + { + if ( txid != zeroid ) + gameheight = komodo_blockheight(hashBlock); + else + { + txid = tx.GetHash(); + //fprintf(stderr,"set txid %s %llu\n",txid.GetHex().c_str(),(long long)CCgettxout(txid,0,1)); + } + if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) == txfee && (unspentv0 == 0 || CCgettxout(txid,0,1,0) == txfee) ) + { + if ( rogue_newgameopreturndecode(buyin,maxplayers,tx.vout[numvouts-1].scriptPubKey) == 'G' ) + { + if ( maxplayers < 1 || maxplayers > ROGUE_MAXPLAYERS || buyin < 0 ) + return(-6); + if ( numvouts > 2*maxplayers+1 ) + { + for (i=0; i playerdata) +{ + struct rogue_player P; int32_t i; char packitemstr[512]; + if ( playerdata.size() > 0 ) + { + for (i=0; i>16,P.level,P.experience,P.dungeonlevel); + for (i=0; i playerdata,uint256 playertxid,uint256 tokenid,std::string symbol,std::string pname,uint256 gametxid) +{ + int32_t i,vout,spentvini,numvouts,n=0; uint256 txid,spenttxid,hashBlock; struct rogue_player P; char packitemstr[512],*datastr=0; UniValue obj(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx; + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + datastr = (char *)malloc(playerdata.size()*2+1); + for (i=0; i= 0 ) + txid = spenttxid; + else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + break; + } + txid = spenttxid; + vout = 0; + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + for (i=0; i ROGUE_MAXITERATIONS ) + break; + } + obj.push_back(Pair("gametxid",gametxid.GetHex())); + if ( txid != playertxid ) + obj.push_back(Pair("batontxid",txid.GetHex())); + obj.push_back(Pair("playertxid",playertxid.GetHex())); + if ( tokenid != zeroid ) + obj.push_back(Pair("tokenid",tokenid.GetHex())); + else obj.push_back(Pair("tokenid",playertxid.GetHex())); + if ( datastr != 0 ) + { + obj.push_back(Pair("data",datastr)); + free(datastr); + } + obj.push_back(Pair("pack",a)); + obj.push_back(Pair("packsize",(int64_t)P.packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P.hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P.strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P.strength>>16))); + obj.push_back(Pair("level",(int64_t)P.level)); + obj.push_back(Pair("experience",(int64_t)P.experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P.dungeonlevel)); + obj.push_back(Pair("chain",symbol)); + obj.push_back(Pair("pname",pname)); + return(obj); +} + +int32_t rogue_iterateplayer(uint256 ®istertxid,uint256 firsttxid,int32_t firstvout,uint256 lasttxid) // retrace playertxid vins to reach highlander <- this verifies player is valid and rogue_playerdataspend makes sure it can only be used once +{ + uint256 spenttxid,txid = firsttxid; int32_t spentvini,n,vout = firstvout; + registertxid = zeroid; + if ( vout < 0 ) + return(-1); + n = 0; + while ( (spentvini= myIsutxo_spent(spenttxid,txid,vout)) == 0 ) + { + txid = spenttxid; + vout = spentvini; + if ( registertxid == zeroid ) + registertxid = txid; + if ( ++n >= ROGUE_MAXITERATIONS ) + { + fprintf(stderr,"rogue_iterateplayer n.%d, seems something is wrong\n",n); + return(-2); + } + } + if ( txid == lasttxid ) + return(0); + else + { + fprintf(stderr,"firsttxid.%s/v%d -> %s != last.%s\n",firsttxid.ToString().c_str(),firstvout,txid.ToString().c_str(),lasttxid.ToString().c_str()); + return(-1); + } +} + +/* + playertxid is whoever owns the nonfungible satoshi and it might have been bought and sold many times. + highlander is the game winning tx with the player data and is the only place where the unique player data exists + origplayergame is the gametxid that ends up being won by the highlander and they are linked directly as the highlander tx spends gametxid.vout0 + */ + +int32_t rogue_playerdata(struct CCcontract_info *cp,uint256 &origplayergame,uint256 &tokenid,CPubKey &pk,std::vector &playerdata,std::string &symbol,std::string &pname,uint256 playertxid) +{ + uint256 origplayertxid,hashBlock,gametxid,registertxid; CTransaction gametx,playertx,highlandertx; std::vector vopret; uint8_t *script,e,f; int32_t i,regslot,gameheight,numvouts,maxplayers; int64_t buyin; + if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 && (numvouts= playertx.vout.size()) > 0 ) + { + if ( (f= rogue_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,playertx.vout[numvouts-1].scriptPubKey)) == 'H' || f == 'Q' ) + { + origplayergame = gametxid; + if ( tokenid != zeroid ) + { + playertxid = tokenid; + if ( myGetTransaction(playertxid,playertx,hashBlock) == 0 || (numvouts= playertx.vout.size()) <= 0 ) + { + fprintf(stderr,"couldnt get tokenid.%s\n",playertxid.GetHex().c_str()); + return(-2); + } + } + if ( rogue_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) + { + //fprintf(stderr,"playertxid.%s got vin.%s/v%d gametxid.%s iterate.%d\n",playertxid.ToString().c_str(),playertx.vin[1].prevout.hash.ToString().c_str(),(int32_t)playertx.vin[1].prevout.n-maxplayers,gametxid.ToString().c_str(),rogue_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid)); + if ( (tokenid != zeroid || playertx.vin[1].prevout.hash == gametxid) && rogue_iterateplayer(registertxid,gametxid,playertx.vin[1].prevout.n-maxplayers,playertxid) == 0 ) + { + // if registertxid has vin from pk, it can be used + return(0); + } else fprintf(stderr,"hash mismatch or illegal gametxid\n"); + } else fprintf(stderr,"invalid game %s\n",gametxid.GetHex().c_str()); + } //else fprintf(stderr,"invalid player funcid.%c\n",f); + } else fprintf(stderr,"couldnt get playertxid.%s\n",playertxid.GetHex().c_str()); + return(-1); +} + +int32_t rogue_playerdataspend(CMutableTransaction &mtx,uint256 playertxid,int32_t vout,uint256 origplayergame) +{ + int64_t txfee = 10000; CTransaction tx; uint256 hashBlock; + if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation + { + mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); + return(0); + } + else + { + vout = 0; + if ( myGetTransaction(playertxid,tx,hashBlock) != 0 && tx.vout[vout].nValue == 1 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) + { + if ( CCgettxout(playertxid,vout,1,0) == 1 ) // not sure if this is enough validation + { + mtx.vin.push_back(CTxIn(playertxid,vout,CScript())); + return(0); + } + } + return(-1); + } +} + +int32_t rogue_findbaton(struct CCcontract_info *cp,uint256 &playertxid,char **keystrokesp,int32_t &numkeys,int32_t ®slot,std::vector &playerdata,uint256 &batontxid,int32_t &batonvout,int64_t &batonvalue,int32_t &batonht,uint256 gametxid,CTransaction gametx,int32_t maxplayers,char *destaddr,int32_t &numplayers,std::string &symbol,std::string &pname) +{ + int32_t i,numvouts,spentvini,n,matches = 0; CPubKey pk; uint256 tid,active,spenttxid,tokenid,hashBlock,txid,origplayergame; CTransaction spenttx,matchtx,batontx; std::vector checkdata; CBlockIndex *pindex; char ccaddr[64],*keystrokes=0; + batonvalue = numkeys = numplayers = batonht = 0; + playertxid = batontxid = zeroid; + if ( keystrokesp != 0 ) + *keystrokesp = 0; + for (i=0; i= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + { + numplayers++; + Getscriptaddress(ccaddr,spenttx.vout[0].scriptPubKey); + if ( strcmp(destaddr,ccaddr) == 0 ) + { + matches++; + regslot = i; + matchtx = spenttx; + } //else fprintf(stderr,"%d+1 doesnt match %s vs %s\n",i,ccaddr,destaddr); + } //else fprintf(stderr,"%d+1 couldnt find spenttx.%s\n",i,spenttxid.GetHex().c_str()); + } //else fprintf(stderr,"%d+1 unspent\n",i); + } + if ( matches == 1 ) + { + numvouts = matchtx.vout.size(); + //fprintf(stderr,"matchtxid.%s matches.%d numvouts.%d\n",matchtx.GetHash().GetHex().c_str(),matches,numvouts); + if ( rogue_registeropretdecode(txid,tokenid,playertxid,matchtx.vout[numvouts-1].scriptPubKey) == 'R' )//&& txid == gametxid ) + { + //fprintf(stderr,"tokenid.%s txid.%s vs gametxid.%s player.%s\n",tokenid.GetHex().c_str(),txid.GetHex().c_str(),gametxid.GetHex().c_str(),playertxid.GetHex().c_str()); + if ( tokenid != zeroid ) + active = tokenid; + else active = playertxid; + if ( active == zeroid || rogue_playerdata(cp,origplayergame,tid,pk,playerdata,symbol,pname,active) == 0 ) + { + txid = matchtx.GetHash(); + //fprintf(stderr,"scan forward active.%s spenttxid.%s\n",active.GetHex().c_str(),txid.GetHex().c_str()); + n = 0; + while ( CCgettxout(txid,0,1,0) < 0 ) + { + spenttxid = zeroid; + spentvini = -1; + if ( (spentvini= myIsutxo_spent(spenttxid,txid,0)) >= 0 ) + txid = spenttxid; + else + { + if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,0) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + return(-2); + } + } + txid = spenttxid; + //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + if ( spentvini != 0 ) // game is over? + { + return(0); + } + if ( keystrokesp != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) + { + uint256 g,b; CPubKey p; std::vector k; + if ( rogue_keystrokesopretdecode(g,b,p,k,spenttx.vout[spenttx.vout.size()-1].scriptPubKey) == 'K' ) + { + keystrokes = (char *)realloc(keystrokes,numkeys + (int32_t)k.size()); + for (i=0; i= ROGUE_MAXITERATIONS ) + { + fprintf(stderr,"rogue_findbaton n.%d, seems something is wrong\n",n); + return(-5); + } + } + //fprintf(stderr,"set baton %s\n",txid.GetHex().c_str()); + batontxid = txid; + batonvout = 0; // not vini + // how to detect timeout, bailedout, highlander + hashBlock = zeroid; + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 0 ) + { + if ( hashBlock == zeroid ) + batonht = komodo_nextheight(); + else if ( (pindex= komodo_blockindex(hashBlock)) == 0 ) + return(-4); + else batonht = pindex->GetHeight(); + batonvalue = batontx.vout[0].nValue; + //printf("batonht.%d keystrokes[%d]\n",batonht,numkeys); + return(0); + } else fprintf(stderr,"couldnt find baton\n"); + } else fprintf(stderr,"error with playerdata\n"); + } else fprintf(stderr,"findbaton opret error\n"); + } + return(-1); +} + +int32_t rogue_playersalive(int32_t &openslots,int32_t &numplayers,uint256 gametxid,int32_t maxplayers,int32_t gameht,CTransaction gametx) +{ + int32_t i,n,vout,spentvini,registration_open = 0,alive = 0; CTransaction tx; uint256 txid,spenttxid,hashBlock; CBlockIndex *pindex; uint64_t txfee = 10000; + numplayers = openslots = 0; + if ( komodo_nextheight() <= gameht+ROGUE_MAXKEYSTROKESGAP ) + registration_open = 1; + for (i=0; i= 0 ) + txid = spenttxid; + else if ( myIsutxo_spentinmempool(spenttxid,spentvini,txid,vout) == 0 || spenttxid == zeroid ) + { + fprintf(stderr,"mempool tracking error %s/v0\n",txid.ToString().c_str()); + break; + } + txid = spenttxid; + vout = 0; + //fprintf(stderr,"n.%d next txid.%s/v%d\n",n,txid.GetHex().c_str(),spentvini); + if ( spentvini != 0 ) + break; + if ( n++ > ROGUE_MAXITERATIONS ) + break; + } + if ( txid != zeroid ) + { + if ( myGetTransaction(txid,tx,hashBlock) != 0 ) + { + if ( (pindex= komodo_blockindex(hashBlock)) != 0 ) + { + if ( pindex->GetHeight() <= gameht+ROGUE_MAXKEYSTROKESGAP ) + alive++; + } + } + } + } + } + else if ( registration_open != 0 ) + openslots++; + } + //fprintf(stderr,"numalive.%d openslots.%d\n",alive,openslots); + return(alive); +} + +uint64_t rogue_gamefields(UniValue &obj,int64_t maxplayers,int64_t buyin,uint256 gametxid,char *myrogueaddr) +{ + CBlockIndex *pindex; int32_t ht,openslots,delay,numplayers; uint256 hashBlock; uint64_t seed=0; char cmd[512]; CTransaction tx; + if ( myGetTransaction(gametxid,tx,hashBlock) != 0 && (pindex= komodo_blockindex(hashBlock)) != 0 ) + { + ht = pindex->GetHeight(); + delay = ROGUE_REGISTRATION * (maxplayers > 1); + obj.push_back(Pair("height",ht)); + obj.push_back(Pair("start",ht+delay)); + if ( komodo_nextheight() > ht+delay ) + { + if ( (pindex= komodo_chainactive(ht+delay)) != 0 ) + { + hashBlock = pindex->GetBlockHash(); + obj.push_back(Pair("starthash",hashBlock.ToString())); + memcpy(&seed,&hashBlock,sizeof(seed)); + seed &= (1LL << 62) - 1; + obj.push_back(Pair("seed",(int64_t)seed)); + if ( rogue_iamregistered(maxplayers,gametxid,tx,myrogueaddr) > 0 ) + sprintf(cmd,"cc/rogue/rogue %llu %s",(long long)seed,gametxid.ToString().c_str()); + else sprintf(cmd,"./komodo-cli -ac_name=%s cclib register %d \"[%%22%s%%22]\"",ASSETCHAINS_SYMBOL,EVAL_ROGUE,gametxid.ToString().c_str()); + obj.push_back(Pair("run",cmd)); + } + } + obj.push_back(Pair("alive",rogue_playersalive(openslots,numplayers,gametxid,maxplayers,ht,tx))); + obj.push_back(Pair("openslots",openslots)); + obj.push_back(Pair("numplayers",numplayers)); + obj.push_back(Pair("buyins",ValueFromAmount(rogue_buyins(gametxid,maxplayers)))); + } + obj.push_back(Pair("maxplayers",maxplayers)); + obj.push_back(Pair("buyin",ValueFromAmount(buyin))); + return(seed); +} + +void rogue_gameplayerinfo(struct CCcontract_info *cp,UniValue &obj,uint256 gametxid,CTransaction gametx,int32_t vout,int32_t maxplayers,char *myrogueaddr) +{ + // identify if bailout or quit or timed out + uint256 batontxid,spenttxid,gtxid,ptxid,tokenid,hashBlock,playertxid; CTransaction spenttx,batontx; int32_t numplayers,regslot,numkeys,batonvout,batonht,retval; int64_t batonvalue; std::vector playerdata; char destaddr[64]; std::string symbol,pname; + destaddr[0] = 0; + if ( myIsutxo_spent(spenttxid,gametxid,vout) >= 0 ) + { + if ( myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 0 ) + Getscriptaddress(destaddr,spenttx.vout[0].scriptPubKey); + } + obj.push_back(Pair("slot",(int64_t)vout-1)); + if ( (retval= rogue_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,destaddr,numplayers,symbol,pname)) == 0 ) + { + if ( CCgettxout(gametxid,maxplayers+vout,1,0) == 10000 ) + { + if ( myGetTransaction(batontxid,batontx,hashBlock) != 0 && batontx.vout.size() > 1 ) + { + if ( rogue_registeropretdecode(gtxid,tokenid,ptxid,batontx.vout[batontx.vout.size()-1].scriptPubKey) == 'R' && ptxid == playertxid && gtxid == gametxid ) + obj.push_back(Pair("status","registered")); + else obj.push_back(Pair("status","alive")); + } else obj.push_back(Pair("status","error")); + } else obj.push_back(Pair("status","finished")); + obj.push_back(Pair("baton",batontxid.ToString())); + obj.push_back(Pair("tokenid",tokenid.ToString())); + obj.push_back(Pair("batonaddr",destaddr)); + obj.push_back(Pair("ismine",strcmp(myrogueaddr,destaddr)==0)); + obj.push_back(Pair("batonvout",(int64_t)batonvout)); + obj.push_back(Pair("batonvalue",ValueFromAmount(batonvalue))); + obj.push_back(Pair("batonht",(int64_t)batonht)); + if ( playerdata.size() > 0 ) + obj.push_back(Pair("player",rogue_playerobj(playerdata,playertxid,tokenid,symbol,pname,gametxid))); + } else fprintf(stderr,"findbaton err.%d\n",retval); +} + +int64_t rogue_registrationbaton(CMutableTransaction &mtx,uint256 gametxid,CTransaction gametx,int32_t maxplayers) +{ + int32_t vout,j,r; int64_t nValue; + if ( gametx.vout.size() > maxplayers+1 ) + { + r = rand() % maxplayers; + for (j=0; j 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +UniValue rogue_newgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey roguepk,mypk; char *jsonstr; uint64_t inputsum,change,required,buyin=0; int32_t i,n,maxplayers = 1; + if ( txfee == 0 ) + txfee = 10000; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + maxplayers = juint(jitem(params,0),0); + if ( n > 1 ) + buyin = jdouble(jitem(params,1),0) * COIN + 0.0000000049; + } + } + if ( maxplayers < 1 || maxplayers > ROGUE_MAXPLAYERS ) + return(cclib_error(result,"illegal maxplayers")); + mypk = pubkey2pk(Mypubkey()); + roguepk = GetUnspendable(cp,0); + rogue_univalue(result,"newgame",maxplayers,buyin); + required = (3*txfee + maxplayers*(ROGUE_REGISTRATIONSIZE+txfee)); + if ( (inputsum= AddCClibInputs(cp,mtx,roguepk,required,16,cp->unspendableCCaddr,1)) >= required ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,roguepk)); // for highlander TCBOO creation + for (i=0; ievalcode,ROGUE_REGISTRATIONSIZE,roguepk,roguepk)); + for (i=0; ievalcode,txfee,roguepk,roguepk)); + if ( (change= inputsum - required) >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,roguepk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,rogue_newgameopret(buyin,maxplayers)); + return(rogue_rawtxresult(result,rawtx,1)); + } + else return(cclib_error(result,"illegal maxplayers")); + return(result); +} + +UniValue rogue_playerinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); std::vector playerdata; uint256 playertxid,tokenid,origplayergame;int32_t n; CPubKey pk; bits256 t; std::string symbol,pname; + result.push_back(Pair("result","success")); + rogue_univalue(result,"playerinfo",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + playertxid = juint256(jitem(params,0)); + if ( rogue_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) + return(cclib_error(result,"invalid playerdata")); + result.push_back(Pair("player",rogue_playerobj(playerdata,playertxid,tokenid,symbol,pname,origplayergame))); + } else return(cclib_error(result,"no playertxid")); + return(result); + } else return(cclib_error(result,"couldnt reparse params")); +} + +UniValue rogue_register(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + // vin0 -> ROGUE_REGISTRATIONSIZE 1of2 registration baton from creategame + // vin1 -> optional nonfungible character vout @ + // vin2 -> original creation TCBOO playerdata used + // vin3+ -> buyin + // vout0 -> keystrokes/completion baton + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); char destaddr[64],coinaddr[64]; uint256 tokenid,gametxid,origplayergame,playertxid,hashBlock; int32_t err,maxplayers,gameheight,n,numvouts,vout=1; int64_t inputsum,buyin,CCchange=0; CPubKey pk,mypk,roguepk,burnpk; CTransaction tx,playertx; std::vector playerdata; std::string rawtx,symbol,pname; bits256 t; + + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + roguepk = GetUnspendable(cp,0); + rogue_univalue(result,"register",-1,-1); + playertxid = tokenid = zeroid; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + if ( (err= rogue_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1)) == 0 ) + { + if ( n > 1 ) + { + playertxid = juint256(jitem(params,1)); + if ( rogue_playerdata(cp,origplayergame,tokenid,pk,playerdata,symbol,pname,playertxid) < 0 ) + return(cclib_error(result,"couldnt extract valid playerdata")); + if ( tokenid != zeroid ) // if it is tokentransfer this will be 0 + vout = 1; + } + if ( komodo_nextheight() > gameheight + ROGUE_MAXKEYSTROKESGAP ) + return(cclib_error(result,"didnt register in time, ROGUE_MAXKEYSTROKESGAP")); + rogue_univalue(result,0,maxplayers,buyin); + GetCCaddress1of2(cp,coinaddr,roguepk,mypk); + if ( rogue_iamregistered(maxplayers,gametxid,tx,coinaddr) > 0 ) + return(cclib_error(result,"already registered")); + if ( (inputsum= rogue_registrationbaton(mtx,gametxid,tx,maxplayers)) != ROGUE_REGISTRATIONSIZE ) + return(cclib_error(result,"couldnt find available registration baton")); + else if ( playertxid != zeroid && rogue_playerdataspend(mtx,playertxid,vout,origplayergame) < 0 ) + return(cclib_error(result,"couldnt find playerdata to spend")); + else if ( buyin > 0 && AddNormalinputs(mtx,mypk,buyin,64) < buyin ) + return(cclib_error(result,"couldnt find enough normal funds for buyin")); + if ( tokenid != zeroid ) + { + mtx.vin.push_back(CTxIn(tokenid,0)); // spending cc marker as token is burned + char unspendableTokenAddr[64]; uint8_t tokenpriv[32]; struct CCcontract_info *cpTokens, tokensC; + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + CPubKey unspPk = GetUnspendable(cpTokens, tokenpriv); + GetCCaddress(cpTokens, unspendableTokenAddr, unspPk); + CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); + } + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,buyin + inputsum - txfee,roguepk,mypk)); + GetCCaddress1of2(cp,destaddr,roguepk,roguepk); + CCaddr1of2set(cp,roguepk,roguepk,cp->CCpriv,destaddr); + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode, 1, burnpk)); + + uint8_t e, funcid; uint256 tid; std::vector voutPubkeys, voutPubkeysEmpty; int32_t didtx = 0; + CScript opretRegister = rogue_registeropret(gametxid, playertxid); + if ( playertxid != zeroid ) + { + voutPubkeysEmpty.push_back(burnpk); + if ( myGetTransaction(playertxid,playertx,hashBlock) != 0 ) + { + std::vector> oprets; + if ( (funcid= DecodeTokenOpRet(playertx.vout.back().scriptPubKey, e, tid, voutPubkeys, oprets)) != 0) + { // if token in the opret + didtx = 1; + if ( funcid == 'c' ) + tid = tokenid == zeroid ? playertxid : tokenid; + vscript_t vopretRegister; + GetOpReturnData(opretRegister, vopretRegister); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, + EncodeTokenOpRet(tid, voutPubkeysEmpty /*=never spent*/, std::make_pair(OPRETID_ROGUEGAMEDATA, vopretRegister))); + } + } + } + if ( didtx == 0 ) + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, opretRegister); + + return(rogue_rawtxresult(result,rawtx,1)); + } else return(cclib_error(result,"invalid gametxid")); + } else return(cclib_error(result,"no gametxid")); + } else return(cclib_error(result,"couldnt reparse params")); +} + +UniValue rogue_keystrokes(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + // vin0 -> baton from registration or previous keystrokes + // vout0 -> new baton + // opret -> user input chars + // being killed should auto broadcast (possible to be suppressed?) + // respawn to be prevented by including timestamps + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); + UniValue result(UniValue::VOBJ); CPubKey roguepk,mypk; uint256 gametxid,playertxid,batontxid; int64_t batonvalue,buyin; std::vector keystrokes,playerdata; int32_t numplayers,regslot,numkeys,batonht,batonvout,n,elapsed,gameheight,maxplayers; CTransaction tx; CTxOut txout; char *keystrokestr,destaddr[64]; std::string rawtx,symbol,pname; bits256 t; uint8_t mypriv[32]; + // if ( txfee == 0 ) + txfee = 1000; // smaller than normal on purpose + rogue_univalue(result,"keystrokes",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) == 2 && (keystrokestr= jstr(jitem(params,1),0)) != 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + result.push_back(Pair("keystrokes",keystrokestr)); + keystrokes = ParseHex(keystrokestr); + mypk = pubkey2pk(Mypubkey()); + roguepk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,destaddr,roguepk,mypk); + if ( rogue_isvalidgame(cp,gameheight,tx,buyin,maxplayers,gametxid,1) == 0 ) + { + if ( rogue_findbaton(cp,playertxid,0,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,tx,maxplayers,destaddr,numplayers,symbol,pname) == 0 ) + { + result.push_back(Pair("batontxid",batontxid.GetHex())); + result.push_back(Pair("playertxid",playertxid.GetHex())); + if ( maxplayers == 1 || nextheight <= batonht+ROGUE_MAXKEYSTROKESGAP ) + { + mtx.vin.push_back(CTxIn(batontxid,batonvout,CScript())); //this validates user if pk + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,batonvalue-txfee,roguepk,mypk)); + Myprivkey(mypriv); + CCaddr1of2set(cp,roguepk,mypk,mypriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,rogue_keystrokesopret(gametxid,batontxid,mypk,keystrokes)); + //fprintf(stderr,"KEYSTROKES.(%s)\n",rawtx.c_str()); + return(rogue_rawtxresult(result,rawtx,1)); + } else return(cclib_error(result,"keystrokes tx was too late")); + } else return(cclib_error(result,"couldnt find batontxid")); + } else return(cclib_error(result,"invalid gametxid")); + } else return(cclib_error(result,"couldnt reparse params")); +} + +char *rogue_extractgame(int32_t makefiles,char *str,int32_t *numkeysp,std::vector &newdata,uint64_t &seed,uint256 &playertxid,struct CCcontract_info *cp,uint256 gametxid,char *rogueaddr) +{ + CPubKey roguepk; int32_t i,num,retval,maxplayers,gameheight,batonht,batonvout,numplayers,regslot,numkeys,err; std::string symbol,pname; CTransaction gametx; int64_t buyin,batonvalue; char fname[64],*keystrokes = 0; std::vector playerdata; uint256 batontxid; FILE *fp; uint8_t newplayer[10000]; struct rogue_player P,endP; + roguepk = GetUnspendable(cp,0); + *numkeysp = 0; + seed = 0; + num = numkeys = 0; + playertxid = zeroid; + str[0] = 0; + if ( (err= rogue_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0)) == 0 ) + { + if ( (retval= rogue_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,rogueaddr,numplayers,symbol,pname)) == 0 ) + { + UniValue obj; + seed = rogue_gamefields(obj,maxplayers,buyin,gametxid,rogueaddr); +//fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d playertxid.%s\n",pname.size()!=0?pname.c_str():Rogue_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size(),playertxid.GetHex().c_str()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i no playerdata\n"); + newdata.resize(0); + *numkeysp = numkeys; + return(keystrokes); + /* P.gold = (P.gold * 8) / 10; + if ( keystrokes != 0 ) + { + free(keystrokes); + keystrokes = 0; + *numkeysp = 0; + return(keystrokes); + }*/ + } + else + { + sprintf(str,"$$$gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",endP.gold,endP.hitpoints,endP.strength&0xffff,endP.strength>>16,endP.level,endP.experience,endP.dungeonlevel); + //fprintf(stderr,"%s\n",str); + *numkeysp = numkeys; + return(keystrokes); + } + } else num = 0; + } + else + { + fprintf(stderr,"extractgame: couldnt find baton keystrokes.%p retval.%d\n",keystrokes,retval); + if ( keystrokes != 0 ) + free(keystrokes), keystrokes = 0; + } + } else fprintf(stderr,"extractgame: invalid game\n"); + //fprintf(stderr,"extract %s\n",gametxid.GetHex().c_str()); + return(0); +} + +UniValue rogue_extract(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); CPubKey pk,roguepk; int32_t i,n,numkeys,flag = 0; uint64_t seed; char str[512],rogueaddr[64],*pubstr,*hexstr,*keystrokes = 0; std::vector newdata; uint256 gametxid,playertxid; FILE *fp; uint8_t pub33[33]; + pk = pubkey2pk(Mypubkey()); + roguepk = GetUnspendable(cp,0); + result.push_back(Pair("name","rogue")); + result.push_back(Pair("method","extract")); + rogueaddr[0] = 0; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + if ( n == 2 ) + { + if ( (pubstr= jstr(jitem(params,1),0)) != 0 ) + { + if (strlen(pubstr) == 66 ) + { + decode_hex(pub33,33,pubstr); + pk = buf2pk(pub33); + } + else if ( strlen(pubstr) < 36 ) + strcpy(rogueaddr,pubstr); + } + //fprintf(stderr,"gametxid.%s %s\n",gametxid.GetHex().c_str(),pubstr); + } + if ( rogueaddr[0] == 0 ) + GetCCaddress1of2(cp,rogueaddr,roguepk,pk); + result.push_back(Pair("rogueaddr",rogueaddr)); + str[0] = 0; + if ( (keystrokes= rogue_extractgame(1,str,&numkeys,newdata,seed,playertxid,cp,gametxid,rogueaddr)) != 0 ) + { + result.push_back(Pair("status","success")); + flag = 1; + hexstr = (char *)malloc(numkeys*2 + 1); + for (i=0; iamulet != 0 ) + mult *= 5; + dungeonlevel = P->dungeonlevel; + if ( P->amulet != 0 && dungeonlevel < 26 ) + dungeonlevel = 26; + if ( dungeonlevel > 42 ) + dungeonlevel = 42; + cashout = (uint64_t)P->gold * P->gold * mult * dungeonlevel; + return(cashout); +} + +int32_t rogue_playerdata_validate(int64_t *cashoutp,uint256 &playertxid,struct CCcontract_info *cp,std::vector playerdata,uint256 gametxid,CPubKey pk) +{ + static uint32_t good,bad; static uint256 prevgame; + char str[512],*keystrokes,rogueaddr[64],str2[67],fname[64]; int32_t i,dungeonlevel,numkeys; std::vector newdata; uint64_t seed,mult = 10; CPubKey roguepk; struct rogue_player P; + *cashoutp = 0; + roguepk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,rogueaddr,roguepk,pk); + if ( (keystrokes= rogue_extractgame(0,str,&numkeys,newdata,seed,playertxid,cp,gametxid,rogueaddr)) != 0 ) + { + free(keystrokes); + sprintf(fname,"rogue.%llu.pack",(long long)seed); + remove(fname); + for (i=0; i no playerdata, good.%d bad.%d\n",good,bad); + } + *cashoutp = 0; + return(0); + } + } + if ( gametxid != prevgame ) + { + prevgame = gametxid; + bad++; + disp_playerdata(newdata); + disp_playerdata(playerdata); + fprintf(stderr,"%s playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d\n",gametxid.GetHex().c_str(),P.gold,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel); + fprintf(stderr,"newdata[%d] != playerdata[%d], numkeys.%d %s pub.%s playertxid.%s good.%d bad.%d\n",(int32_t)newdata.size(),(int32_t)playerdata.size(),numkeys,rogueaddr,pubkey33_str(str2,(uint8_t *)&pk),playertxid.GetHex().c_str(),good,bad); + } + } + sprintf(fname,"rogue.%llu.pack",(long long)seed); + remove(fname); + //fprintf(stderr,"no keys rogue_extractgame %s\n",gametxid.GetHex().c_str()); + return(-1); +} + +UniValue rogue_finishgame(uint64_t txfee,struct CCcontract_info *cp,cJSON *params,char *method) +{ + //vin0 -> highlander vout from creategame TCBOO + //vin1 -> keystrokes baton of completed game, must be last to quit or first to win, only spent registration batons matter. If more than 60 blocks since last keystrokes, it is forfeit + //vins2+ -> rest of unspent registration utxo so all newgame vouts are spent + //vout0 -> nonfungible character with pack @ + //vout1 -> 1% ingame gold and all the buyins + + // detect if last to bailout + // vin0 -> kestrokes baton of completed game with Q + // vout0 -> playerdata marker + // vout0 -> 1% ingame gold + // get any playerdata, get all keystrokes, replay game and compare final state + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx,symbol,pname; CTransaction gametx; uint64_t seed,mult; int64_t buyin,batonvalue,inputsum,cashout=0,CCchange=0; int32_t i,err,gameheight,tmp,numplayers,regslot,n,num,dungeonlevel,numkeys,maxplayers,batonht,batonvout; char myrogueaddr[64],*keystrokes = 0; std::vector playerdata,newdata,nodata; uint256 batontxid,playertxid,gametxid; CPubKey mypk,roguepk; uint8_t player[10000],mypriv[32],funcid; + struct CCcontract_info *cpTokens, tokensC; + + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + roguepk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,myrogueaddr,roguepk,mypk); + result.push_back(Pair("name","rogue")); + result.push_back(Pair("method",method)); + result.push_back(Pair("myrogueaddr",myrogueaddr)); + mult = 10; //100000; + if ( strcmp(method,"bailout") == 0 ) + funcid = 'Q'; + else funcid = 'H'; + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + gametxid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",gametxid.GetHex())); + if ( (err= rogue_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,1)) == 0 ) + { + if ( rogue_findbaton(cp,playertxid,&keystrokes,numkeys,regslot,playerdata,batontxid,batonvout,batonvalue,batonht,gametxid,gametx,maxplayers,myrogueaddr,numplayers,symbol,pname) == 0 ) + { + UniValue obj; struct rogue_player P; + seed = rogue_gamefields(obj,maxplayers,buyin,gametxid,myrogueaddr); + fprintf(stderr,"(%s) found baton %s numkeys.%d seed.%llu playerdata.%d\n",pname.size()!=0?pname.c_str():Rogue_pname.c_str(),batontxid.ToString().c_str(),numkeys,(long long)seed,(int32_t)playerdata.size()); + memset(&P,0,sizeof(P)); + if ( playerdata.size() > 0 ) + { + for (i=0; i 0 ) + { + newdata.resize(num); + for (i=0; i no playerdata\n"); + newdata.resize(0); + //P.gold = (P.gold * 8) / 10; + } + else + { + cpTokens = CCinit(&tokensC, EVAL_TOKENS); + mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens,NULL))); // marker to token cc addr, burnable and validated + mtx.vout.push_back(MakeTokensCC1vout(cp->evalcode,1,mypk)); + cashout = rogue_cashout(&P); + fprintf(stderr,"\nextracted $$$gold.%d -> %.8f ROGUE hp.%d strength.%d/%d level.%d exp.%d dl.%d n.%d amulet.%d\n",P.gold,(double)cashout/COIN,P.hitpoints,P.strength&0xffff,P.strength>>16,P.level,P.experience,P.dungeonlevel,n,P.amulet); + if ( komodo_nextheight() > 77777 && cashout > ROGUE_MAXCASHOUT ) + cashout = ROGUE_MAXCASHOUT; + if ( funcid == 'H' && maxplayers > 1 ) + { + if ( P.amulet == 0 ) + { + if ( numplayers != maxplayers ) + return(cclib_error(result,"numplayers != maxplayers")); + else if ( rogue_playersalive(tmp,tmp,gametxid,maxplayers,gameheight,gametx) > 1 ) + return(cclib_error(result,"highlander must be a winner or last one standing")); + } + cashout *= 2; + cashout += numplayers * buyin; + } + if ( cashout > 0 ) + { + if ( (inputsum= AddCClibInputs(cp,mtx,roguepk,cashout,60,cp->unspendableCCaddr,1)) > cashout ) + CCchange = (inputsum - cashout); + else fprintf(stderr,"couldnt find enough utxos\n"); + } + mtx.vout.push_back(CTxOut(cashout,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + } + } + if ( CCchange + (batonvalue-3*txfee) >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange + (batonvalue-3*txfee),roguepk)); + Myprivkey(mypriv); + CCaddr1of2set(cp,roguepk,mypk,mypriv,myrogueaddr); + CScript opret; + if ( pname.size() == 0 ) + pname = Rogue_pname; + if ( newdata.size() == 0 ) + { + opret = rogue_highlanderopret(funcid, gametxid, regslot, mypk, nodata,pname); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,opret); + //fprintf(stderr,"nodata finalizetx.(%s)\n",rawtx.c_str()); + } + else + { + opret = rogue_highlanderopret(funcid, gametxid, regslot, mypk, newdata,pname); + char seedstr[32]; + sprintf(seedstr,"%llu",(long long)seed); + std::vector vopretNonfungible; + GetOpReturnData(opret, vopretNonfungible); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), std::string(seedstr), gametxid.GetHex(), vopretNonfungible)); + } + return(rogue_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","success")); + } else fprintf(stderr,"illegal game err.%d\n",err); + } else fprintf(stderr,"parameters only n.%d\n",n); + } + return(result); +} + +UniValue rogue_bailout(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + return(rogue_finishgame(txfee,cp,params,(char *)"bailout")); +} + +UniValue rogue_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + return(rogue_finishgame(txfee,cp,params,(char *)"highlander")); +} + +UniValue rogue_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char myrogueaddr[64],str[64]; CPubKey mypk,roguepk; + result.push_back(Pair("name","rogue")); + result.push_back(Pair("method","gameinfo")); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + txid = juint256(jitem(params,0)); + result.push_back(Pair("gametxid",txid.GetHex())); + if ( rogue_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,0) == 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("gameheight",(int64_t)gameheight)); + mypk = pubkey2pk(Mypubkey()); + roguepk = GetUnspendable(cp,0); + GetCCaddress1of2(cp,myrogueaddr,roguepk,mypk); + //fprintf(stderr,"myrogueaddr.%s\n",myrogueaddr); + seed = rogue_gamefields(result,maxplayers,buyin,txid,myrogueaddr); + result.push_back(Pair("seed",(int64_t)seed)); + for (i=0; i > unspentOutputs; + roguepk = GetUnspendable(cp,0); + GetCCaddress(cp,coinaddr,roguepk); + SetCCunspents(unspentOutputs,coinaddr,true); + nextheight = komodo_nextheight(); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( it->second.satoshis != txfee || vout != 0 ) // reject any that are not highlander markers + continue; + if ( rogue_isvalidgame(cp,gameheight,tx,buyin,maxplayers,txid,1) == 0 && nextheight <= gameheight+ROGUE_MAXKEYSTROKESGAP ) + { + rogue_playersalive(openslots,numplayers,txid,maxplayers,gameheight,tx); + if ( openslots > 0 ) + a.push_back(txid.GetHex()); + } + } + result.push_back(Pair("result","success")); + rogue_univalue(result,"pending",-1,-1); + result.push_back(Pair("pending",a)); + result.push_back(Pair("numpending",(int64_t)a.size())); + return(result); +} + +UniValue rogue_players(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int64_t buyin; uint256 tokenid,gametxid,txid,hashBlock; CTransaction playertx,tx; int32_t maxplayers,vout,numvouts; std::vector playerdata; CPubKey roguepk,mypk,pk; std::string symbol,pname; char coinaddr[64]; + std::vector > unspentOutputs; + roguepk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + GetTokensCCaddress(cp,coinaddr,mypk); + SetCCunspents(unspentOutputs,coinaddr,true); + rogue_univalue(result,"players",-1,-1); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( it->second.satoshis != 1 || vout > 1 ) + continue; + if ( rogue_playerdata(cp,gametxid,tokenid,pk,playerdata,symbol,pname,txid) == 0 )//&& pk == mypk ) + { + a.push_back(txid.GetHex()); + //a.push_back(Pair("playerdata",rogue_playerobj(playerdata))); + } + } + result.push_back(Pair("playerdata",a)); + result.push_back(Pair("numplayerdata",(int64_t)a.size())); + return(result); +} + +UniValue rogue_games(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR),b(UniValue::VARR); uint256 txid,hashBlock,gametxid,tokenid,playertxid; int32_t vout,maxplayers,gameheight,numvouts; CPubKey roguepk,mypk; char coinaddr[64]; CTransaction tx,gametx; int64_t buyin; + std::vector > addressIndex; + //std::vector > unspentOutputs; + roguepk = GetUnspendable(cp,0); + mypk = pubkey2pk(Mypubkey()); + GetCCaddress1of2(cp,coinaddr,roguepk,mypk); + //SetCCunspents(unspentOutputs,coinaddr); + SetCCtxids(addressIndex,coinaddr,true); + rogue_univalue(result,"games",-1,-1); + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + //for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( vout == 0 ) + { + if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( rogue_registeropretdecode(gametxid,tokenid,playertxid,tx.vout[numvouts-1].scriptPubKey) == 'R' ) + { + if ( rogue_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,gametxid,0) == 0 ) + { + if ( CCgettxout(txid,vout,1,0) < 0 ) + b.push_back(gametxid.GetHex()); + else a.push_back(gametxid.GetHex()); + } + } + } + } + } + result.push_back(Pair("pastgames",b)); + result.push_back(Pair("games",a)); + result.push_back(Pair("numgames",(int64_t)(a.size()+b.size()))); + return(result); +} + +UniValue rogue_setname(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t n; char *namestr = 0; + rogue_univalue(result,"setname",-1,-1); + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 0 ) + { + if ( (namestr= jstri(params,0)) != 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("pname",namestr)); + Rogue_pname = namestr; + return(result); + } + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt get name")); + return(result); +} + +bool rogue_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + CScript scriptPubKey; std::vector vopret; uint8_t *script,e,f,funcid,tokentx=0; int32_t i,maxplayers,enabled = 0,decoded=0,regslot,ind,err,dispflag,gameheight,score,numvouts; CTransaction vintx,gametx; CPubKey pk; uint256 hashBlock,gametxid,txid,tokenid,batontxid,playertxid,ptxid; int64_t buyin,cashout; std::vector playerdata,keystrokes; std::string symbol,pname; + if ( strcmp(ASSETCHAINS_SYMBOL,"ROGUE") == 0 ) + { + if (height < 21274 ) + return(true); + else if ( height > 50000 ) + enabled = 1; + } else enabled = 1; + if ( (numvouts= tx.vout.size()) > 1 ) + { + txid = tx.GetHash(); + if ( txid == Parseuint256("1ae04dc0c5f2fca2053819a3a1b2efe5d355c34f58d6f16d59e5e2573e7baf7f") || txid == Parseuint256("2a34b36cc1292aecfaabdad79b42cab9989fa6dcc87ac8ca88aa6162dab1e2c4") ) // osx rogue chain ht.50902, 69522 + enabled = 0; + scriptPubKey = tx.vout[numvouts-1].scriptPubKey; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 ) + { + script = (uint8_t *)vopret.data(); + funcid = script[1]; + if ( (e= script[0]) == EVAL_TOKENS ) + { + tokentx = funcid; + if ( (funcid= rogue_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,scriptPubKey)) == 0 ) + { + if ( (funcid= rogue_registeropretdecode(gametxid,tokenid,playertxid,scriptPubKey)) == 0 ) + { + fprintf(stderr,"ht.%d couldnt decode tokens opret (%c)\n",height,script[1]); + } else e = EVAL_ROGUE, decoded = 1; + } else e = EVAL_ROGUE, decoded = 1; + } + if ( e == EVAL_ROGUE ) + { + //fprintf(stderr,"ht.%d rogue.(%c)\n",height,script[1]); + if ( decoded == 0 ) + { + switch ( funcid ) + { + case 'G': // seems just need to make sure no vout abuse is left to do + gametx = tx; + gametxid = tx.GetHash(); + gameheight = height; + if ( (err= rogue_isvalidgame(cp,gameheight,gametx,buyin,maxplayers,zeroid,0)) != 0 ) + { + fprintf(stderr,"height.%d %s rogue_isvalidgame error.%d\n",height,gametxid.GetHex().c_str(),err); + return eval->Invalid("invalid gametxid"); + } + //fprintf(stderr,"height.%d %s rogue_isvalidgame\n",height,gametxid.GetHex().c_str()); + return(true); + break; + case 'R': + if ( (funcid= rogue_registeropretdecode(gametxid,tokenid,playertxid,scriptPubKey)) != 'R' ) + { + return eval->Invalid("couldnt decode register opret"); + } + // baton is created + // validation is done below + break; + case 'K': + if ( (funcid= rogue_keystrokesopretdecode(gametxid,batontxid,pk,keystrokes,scriptPubKey)) != 'K' ) + { + return eval->Invalid("couldnt decode keystrokes opret"); + } + // spending the baton proves it is the user if the pk is the signer + return(true); + break; + case 'H': case 'Q': + // done in the next switch statement as there are some H/Q tx with playerdata which would skip this section + break; + default: + return eval->Invalid("illegal rogue non-decoded funcid"); + break; + } + } + switch ( funcid ) + { + case 'R': // register + // verify vout amounts are as they should be and no vins that shouldnt be + return(true); + case 'H': // win + case 'Q': // bailout + if ( (f= rogue_highlanderopretdecode(gametxid,tokenid,regslot,pk,playerdata,symbol,pname,scriptPubKey)) != funcid ) + { + //fprintf(stderr,"height.%d couldnt decode H/Q opret\n",height); + //if ( height > 20000 ) + return eval->Invalid("couldnt decode H/Q opret"); + } + // verify pk belongs to this tx + if ( tokentx == 'c' ) + { + if ( playerdata.size() > 0 ) + { + static char laststr[512]; char cashstr[512]; + if ( rogue_playerdata_validate(&cashout,ptxid,cp,playerdata,gametxid,pk) < 0 ) + { + sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d gametxid.%s player.%s invalid playerdata[%d]\n",tokentx,decoded,height,gametxid.GetHex().c_str(),ptxid.GetHex().c_str(),(int32_t)playerdata.size()); + if ( strcmp(laststr,cashstr) != 0 ) + { + strcpy(laststr,cashstr); + fprintf(stderr,"%s\n",cashstr); + } + if ( enabled != 0 ) + return eval->Invalid("mismatched playerdata"); + } + if ( height > 777777 && cashout > ROGUE_MAXCASHOUT ) + cashout = ROGUE_MAXCASHOUT; + if ( funcid == 'H' ) + { + cashout *= 2; + cashout += rogue_buyins(gametxid,maxplayers); + } + sprintf(cashstr,"tokentx.(%c) decoded.%d ht.%d txid.%s %.8f vs vout2 %.8f",tokentx,decoded,height,txid.GetHex().c_str(),(double)cashout/COIN,(double)tx.vout[2].nValue/COIN); + if ( strcmp(laststr,cashstr) != 0 ) + { + strcpy(laststr,cashstr); + fprintf(stderr,"%s\n",cashstr); + } + } else cashout = 10000; + if ( enabled != 0 && tx.vout[2].nValue > cashout ) + return eval->Invalid("mismatched cashout amount"); + } + if ( funcid == 'Q' ) + { + // verify vin/vout + } + else // 'H' + { + // verify vin/vout and proper payouts + } + return(true); + break; + default: + return eval->Invalid("illegal rogue funcid"); + break; + } + } else return eval->Invalid("illegal evalcode"); + } else return eval->Invalid("opret too small"); + } else return eval->Invalid("not enough vouts"); + return(true); +} + diff --git a/src/cc/sudoku.cpp b/src/cc/sudoku.cpp new file mode 100644 index 000000000..61ea6c545 --- /dev/null +++ b/src/cc/sudoku.cpp @@ -0,0 +1,3052 @@ +// start https://github.com/attractivechaos/plb/blob/master/sudoku/incoming/sudoku_solver.c +/************************************************************************************/ +/* */ +/* Author: Bill DuPree */ +/* Name: sudoku_solver.c */ +/* Language: C */ +/* Date: Feb. 25, 2006 */ +/* Copyright (C) Feb. 25, 2006, All rights reserved. */ +/* */ +/* This is a program that solves Su Doku (aka Sudoku, Number Place, etc.) puzzles */ +/* primarily using deductive logic. It will only resort to trial-and-error and */ +/* backtracking approaches upon exhausting all of its deductive moves. */ +/* */ +/* Puzzles must be of the standard 9x9 variety using the (ASCII) characters '1' */ +/* through '9' for the puzzle solution set. Puzzles should be submitted as 81 */ +/* character strings which, when read left-to-right will fill a 9x9 Sudoku grid */ +/* from left-to-right and top-to-bottom. In the puzzle specification, the */ +/* characters 1 - 9 represent the puzzle "givens" or clues. Any other non-blank */ +/* character represents an unsolved cell. */ +/* */ +/* The puzzle solving algorithm is "home grown." I did not borrow any of the usual */ +/* techniques from the literature, e.g. Donald Knuth's "Dancing Links." Instead */ +/* I "rolled my own" from scratch. As such, its performance can only be blamed */ +/* on yours truly. Still, I feel it is quite fast. On a 333 MHz Pentium II Linux */ +/* box it solves typical medium force puzzles in approximately 800 microseconds or */ +/* about 1,200 puzzles per second, give or take. On an Athlon XP 3000 (Barton core) */ +/* it solves about 6,600 puzzles per sec. */ +/* */ +/* DESCRIPTION OF ALGORITHM: */ +/* */ +/* The puzzle algorithm initially assumes every unsolved cell can assume every */ +/* possible value. It then uses the placement of the givens to refine the choices */ +/* available to each cell. I call this the markup phase. */ +/* */ +/* After markup completes, the algorithm then looks for "singleton" cells with */ +/* values that, due to constraints imposed by the row, column, or 3x3 region, may */ +/* only assume one possible value. Once these cells are assigned values, the */ +/* algorithm returns to the markup phase to apply these changes to the remaining */ +/* candidate solutions. The markup/singleton phases alternate until either no more */ +/* changes occur, or the puzzle is solved. I call the markup/singleton elimination */ +/* loop the "Simple Solver" because in a large percentage of cases it solves the */ +/* puzzle. */ +/* */ +/* If the simple solver portion of the algorithm doesn't produce a solution, then */ +/* more advanced deductive rules are applied. I've implemented two additional rules */ +/* as part of the deductive puzzle solver. The first is subset elimination wherein */ +/* a row/column/region is scanned for X number of cells with X number of matching */ +/* candidate solutions. If such subsets are found in the row, column, or region, */ +/* then the candidates values from the subset may be eliminated from all other */ +/* unsolved cells within the row, column, or region, respectively. */ +/* */ +/* The second advanced deductive rule examines each region looking for candidate */ +/* values that exclusively align themselves along a single row or column, i.e. a */ +/* a vector. If such candidate values are found, then they may be eliminated from */ +/* the cells outside of the region that are part of the aligned row or column. */ +/* */ +/* Note that each of the advanced deductive rules calls all preceeding rules, in */ +/* order, if that advanced rule has effected a change in puzzle markup. */ +/* */ +/* Finally, if no solution is found after iteratively applying all deductive rules, */ +/* then we begin trial-and-error using recursion for backtracking. A working copy */ +/* is created from our puzzle, and using this copy the first cell with the */ +/* smallest number of candidate solutions is chosen. One of the solutions values is */ +/* assigned to that cell, and the solver algorithm is called using this working */ +/* copy as its starting point. Eventually, either a solution, or an impasse is */ +/* reached. */ +/* */ +/* If we reach an impasse, the recursion unwinds and the next trial solution is */ +/* attempted. If a solution is found (at any point) the values for the solution are */ +/* added to a list. Again, so long as we are examining all possibilities, the */ +/* recursion unwinds so that the next trial may be attempted. It is in this manner */ +/* that we enumerate puzzles with multiple solutions. */ +/* */ +/* Note that it is certainly possible to add to the list of applied deductive */ +/* rules. The techniques known as "X-Wing" and "Swordfish" come to mind. On the */ +/* other hand, adding these additional rules will, in all likelihood, slow the */ +/* solver down by adding to the computational burden while producing very few */ +/* results. I've seen the law of diminishing returns even in some of the existing */ +/* rules, e.g. in subset elimination I only look at two and three valued subsets */ +/* because taking it any further than that degraded performance. */ +/* */ +/* PROGRAM INVOCATION: */ +/* */ +/* This program is a console (or command line) based utility and has the following */ +/* usage: */ +/* */ +/* sudoku_solver {-p puzzle | -f } [-o ] */ +/* [-r ] [-1][-a][-c][-g][-l][-m][-n][-s] */ +/* */ +/* where: */ +/* */ +/* -1 Search for first solution, otherwise all solutions are returned */ +/* -a Requests that the answer (solution) be printed */ +/* -c Print a count of solutions for each puzzle */ +/* -d Print the recursive trial depth required to solve the puzzle */ +/* -e Print a step-by-step explanation of the solution(s) */ +/* -f Takes an argument which specifes an input file */ +/* containing one or more unsolved puzzles (default: stdin) */ +/* -G Print the puzzle solution(s) in a 9x9 grid format */ +/* -g Print the number of given clues */ +/* -l Print the recursive trial depth required to solve the puzzle */ +/* -m Print an octal mask for the puzzle givens */ +/* -n Number each result */ +/* -o Specifies an output file for the solutions (default: stdout) */ +/* -p Takes an argument giving a single inline puzzle to be solved */ +/* -r Specifies an output file for unsolvable puzzles */ +/* (default: stderr) */ +/* -s Print the puzzle's score or difficulty rating */ +/* -? Print usage information */ +/* */ +/* The return code is zero if all puzzles had unique solutions, */ +/* (or have one or more solutions when -1 is specified) and non-zero */ +/* when no unique solution exists. */ +/* */ +/* PUZZLE SCORING */ +/* */ +/* A word about puzzle scoring, i.e. rating a puzzle's difficulty, is in order. */ +/* Rating Sudoku puzzles is a rather subjective thing, and thus it is difficult to */ +/* really develop an objective puzzle rating system. I, however, have attempted */ +/* this feat (several times with varying degrees of success ;-) and I think the */ +/* heuristics I'm currently applying aren't too bad for rating the relative */ +/* difficulty of solving a puzzle. */ +/* */ +/* The following is a brief rundown of how it works. The initial puzzle markup is */ +/* a "free" operation, i.e. no points are scored for the first markup pass. I feel */ +/* this is appropriate because a person solving a puzzle will always have to do */ +/* their own eyeballing and scanning of the puzzle. Subsequent passes are */ +/* scored at one point per candidate eliminated because these passes indicate */ +/* that more deductive work is required. Secondly, the "reward" for solving a cell */ +/* is set to one point, and as long as the solution only requires simple markup */ +/* and elimination of singletons, this level of reward remains unchanged. */ +/* */ +/* This reward changes, however, when advanced solving rules are required. Puzzles */ +/* that remain unsolved after the first pass through the simple solver phase have */ +/* a higher "reward", i.e. it is incremented by two. Thus, if subset or vector */ +/* elimination is required, all subsequently solved cells score higher bounties. */ +/* In addition, the successful application of these deductive techniques score */ +/* their own penalties. */ +/* */ +/* Finally, if a trial-and-error approach is called for, then the "reward" is */ +/* incremented by another five points. Thus, the total penalty for each level of */ +/* recursion is an additional seven points per solved cell, i.e. */ +/* (recursive_depth * 7) + 1 points per solved cell. Trial solutions are also */ +/* penalized by a weighting factor that is based upon the number of unsolved cells */ +/* that remain upon reentry to the solver and the depth of recursion. (I've seen a */ +/* pathological puzzle from the "Minimum Sudoku" web site require 16 levels of */ +/* recursion and score a whopping 228,642 points using this scoring system!) */ +/* */ +/* And that brings me to this topic: What do all these points mean? */ +/* */ +/* Well, who knows? This is still subjective, and the weighting system I've chosen */ +/* for point scoring is is largely arbitrary. But based upon feedback from a number */ +/* of individuals, a rough scale of difficulty plays out as follows: */ +/* */ +/* DEGREE OF DIFFICULTY | SCORE */ +/* -------------------------+------------------------------------------ */ +/* TRIVIAL | 80 points or less */ +/* EASY | 81 - 150 points */ +/* MEDIUM | 151 - 250 points */ +/* HARD | 251 - 400 points */ +/* VERY HARD | 401 - 900 points */ +/* DIABOLICAL | 901 and up */ +/* */ +/* Experience shows that puzzles in the HARD category, in a few cases, will */ +/* require a small amount of trial-and-error. The VERY HARD puzzles will likely */ +/* require trial-and-error, and in some cases more than one level of trial-and- */ +/* error. As for the DIABOLICAL puzzles--why waste your time? These are best left */ +/* to masochists, savants and automated solvers. YMMV. */ +/* */ +/* LICENSE: */ +/* */ +/* This program is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* This program is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with this program; if not, write to the Free Software */ +/* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ +/* */ +/* CONTACT: */ +/* */ +/* Email: bdupree@techfinesse.com */ +/* Post: Bill DuPree, 609 Wenonah Ave, Oak Park, IL 60304 USA */ +/* */ +/************************************************************************************/ +/* */ +/* CHANGE LOG: */ +/* */ +/* Rev. Date Init. Description */ +/* -------------------------------------------------------------------------------- */ +/* 1.00 2006-02-25 WD Initial version. */ +/* 1.01 2006-03-13 WD Fixed return code calc. Added signon message. */ +/* 1.10 2006-03-20 WD Added explain option, add'l speed optimizations */ +/* 1.11 2006-03-23 WD More simple speed optimizations, cleanup, bug fixes */ +/* */ +/************************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#define VERSION "1.11" + +#define PUZZLE_ORDER 3 +#define PUZZLE_DIM (PUZZLE_ORDER*PUZZLE_ORDER) +#define PUZZLE_CELLS (PUZZLE_DIM*PUZZLE_DIM) + +/* Command line options */ +#ifdef EXPLAIN +#define OPTIONS "?1acdef:Ggmno:p:r:s" +#else +#define OPTIONS "?1acdf:Ggmno:p:r:s" +#endif +extern char *optarg; +extern int optind, opterr, optopt; + +static char *myname; /* Name that we were invoked under */ + +static FILE *solnfile, *rejects; + +/* This is the list of cell coordinates specified on a row basis */ + +static int const row[PUZZLE_DIM][PUZZLE_DIM] = { + { 0, 1, 2, 3, 4, 5, 6, 7, 8 }, + { 9, 10, 11, 12, 13, 14, 15, 16, 17 }, + { 18, 19, 20, 21, 22, 23, 24, 25, 26 }, + { 27, 28, 29, 30, 31, 32, 33, 34, 35 }, + { 36, 37, 38, 39, 40, 41, 42, 43, 44 }, + { 45, 46, 47, 48, 49, 50, 51, 52, 53 }, + { 54, 55, 56, 57, 58, 59, 60, 61, 62 }, + { 63, 64, 65, 66, 67, 68, 69, 70, 71 }, + { 72, 73, 74, 75, 76, 77, 78, 79, 80 }}; + +/* This is the list of cell coordinates specified on a column basis */ + +static int const col[PUZZLE_DIM][PUZZLE_DIM] = { + { 0, 9, 18, 27, 36, 45, 54, 63, 72 }, + { 1, 10, 19, 28, 37, 46, 55, 64, 73 }, + { 2, 11, 20, 29, 38, 47, 56, 65, 74 }, + { 3, 12, 21, 30, 39, 48, 57, 66, 75 }, + { 4, 13, 22, 31, 40, 49, 58, 67, 76 }, + { 5, 14, 23, 32, 41, 50, 59, 68, 77 }, + { 6, 15, 24, 33, 42, 51, 60, 69, 78 }, + { 7, 16, 25, 34, 43, 52, 61, 70, 79 }, + { 8, 17, 26, 35, 44, 53, 62, 71, 80 }}; + +/* This is the list of cell coordinates specified on a 3x3 region basis */ + +static int const region[PUZZLE_DIM][PUZZLE_DIM] = { + { 0, 1, 2, 9, 10, 11, 18, 19, 20 }, + { 3, 4, 5, 12, 13, 14, 21, 22, 23 }, + { 6, 7, 8, 15, 16, 17, 24, 25, 26 }, + { 27, 28, 29, 36, 37, 38, 45, 46, 47 }, + { 30, 31, 32, 39, 40, 41, 48, 49, 50 }, + { 33, 34, 35, 42, 43, 44, 51, 52, 53 }, + { 54, 55, 56, 63, 64, 65, 72, 73, 74 }, + { 57, 58, 59, 66, 67, 68, 75, 76, 77 }, + { 60, 61, 62, 69, 70, 71, 78, 79, 80 }}; + +/* Flags for cellflags member */ +#define GIVEN 1 +#define FOUND 2 +#define STUCK 3 + +/* Return codes for funcs that modify puzzle markup */ +#define NOCHANGE 0 +#define CHANGE 1 + +typedef struct grd { + short cellflags[PUZZLE_CELLS]; + short solved[PUZZLE_CELLS]; + short cell[PUZZLE_CELLS]; + short tail, givens, exposed, maxlvl, inc, reward; + unsigned int score, solncount; + struct grd *next; +} grid; + +typedef int (*return_soln)(grid *g); + +static grid *soln_list = NULL; + +typedef struct { + short row, col, region; +} cellmap; + +/* Array structure to help map cell index back to row, column, and region */ +static cellmap const map[PUZZLE_CELLS] = { + { 0, 0, 0 }, + { 0, 1, 0 }, + { 0, 2, 0 }, + { 0, 3, 1 }, + { 0, 4, 1 }, + { 0, 5, 1 }, + { 0, 6, 2 }, + { 0, 7, 2 }, + { 0, 8, 2 }, + { 1, 0, 0 }, + { 1, 1, 0 }, + { 1, 2, 0 }, + { 1, 3, 1 }, + { 1, 4, 1 }, + { 1, 5, 1 }, + { 1, 6, 2 }, + { 1, 7, 2 }, + { 1, 8, 2 }, + { 2, 0, 0 }, + { 2, 1, 0 }, + { 2, 2, 0 }, + { 2, 3, 1 }, + { 2, 4, 1 }, + { 2, 5, 1 }, + { 2, 6, 2 }, + { 2, 7, 2 }, + { 2, 8, 2 }, + { 3, 0, 3 }, + { 3, 1, 3 }, + { 3, 2, 3 }, + { 3, 3, 4 }, + { 3, 4, 4 }, + { 3, 5, 4 }, + { 3, 6, 5 }, + { 3, 7, 5 }, + { 3, 8, 5 }, + { 4, 0, 3 }, + { 4, 1, 3 }, + { 4, 2, 3 }, + { 4, 3, 4 }, + { 4, 4, 4 }, + { 4, 5, 4 }, + { 4, 6, 5 }, + { 4, 7, 5 }, + { 4, 8, 5 }, + { 5, 0, 3 }, + { 5, 1, 3 }, + { 5, 2, 3 }, + { 5, 3, 4 }, + { 5, 4, 4 }, + { 5, 5, 4 }, + { 5, 6, 5 }, + { 5, 7, 5 }, + { 5, 8, 5 }, + { 6, 0, 6 }, + { 6, 1, 6 }, + { 6, 2, 6 }, + { 6, 3, 7 }, + { 6, 4, 7 }, + { 6, 5, 7 }, + { 6, 6, 8 }, + { 6, 7, 8 }, + { 6, 8, 8 }, + { 7, 0, 6 }, + { 7, 1, 6 }, + { 7, 2, 6 }, + { 7, 3, 7 }, + { 7, 4, 7 }, + { 7, 5, 7 }, + { 7, 6, 8 }, + { 7, 7, 8 }, + { 7, 8, 8 }, + { 8, 0, 6 }, + { 8, 1, 6 }, + { 8, 2, 6 }, + { 8, 3, 7 }, + { 8, 4, 7 }, + { 8, 5, 7 }, + { 8, 6, 8 }, + { 8, 7, 8 }, + { 8, 8, 8 } +}; + +static const short symtab[1<= '1') && (c <= '9'); } + +#if defined(DEBUG) +static void mypause() +{ + char buf[8]; + printf("\tPress enter -> "); + fgets(buf, 8, stdin); +} +#endif + +#if 0 +/* Generic (and slow) bitcount function */ +static int bitcount(short cell) +{ + int i, count, mask; + + mask = 1; + for (i = count = 0; i < 16; i++) { + if (mask & cell) count++; + mask <<= 1; + } + return count; +} +#endif + +/*****************************************************/ +/* Return the number of '1' bits in a cell. */ +/* Rather than count bits, do a quick table lookup. */ +/* Warning: Only valid for 9 low order bits. */ +/*****************************************************/ + +static inline short bitcount(short cell) +{ + static const short bcounts[512] = { + 0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8, + 1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8, + 2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8, + 3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8, + 4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8,5,6,6,7,6,7,7,8,6,7,7,8,7,8,8,9}; + + return bcounts[cell]; +} + +#ifdef EXPLAIN + +/**************************************************/ +/* Indent two spaces for each level of recursion. */ +/**************************************************/ +static inline void explain_indent(FILE *h) +{ + int i; + + for (i = 0; i < lvl-1; i++) fprintf(h, " "); +} + +/******************************************************************/ +/* Construct a string representing the possible values a cell may */ +/* contain according to current markup. */ +/******************************************************************/ +static char *clues(short cell) +{ + int i, m, multi, mask; + static char buf[64], *p; + + multi = m = bitcount(cell); + + if (!multi) return "NULL"; + + if (multi > 1) { + strcpy(buf, "tuple ("); + } + else { + strcpy(buf, "value "); + } + + p = buf + strlen(buf); + + for (mask = i = 1; i <= PUZZLE_DIM; i++) { + if (mask & cell) { + *p++ = symtab[mask]; + multi -= 1; + if (multi) { *p++ = ','; *p++ = ' '; } + } + mask <<= 1; + } + if (m > 1) *p++ = ')'; + *p = 0; + return buf; +} + +/*************************************************************/ +/* Explain removal of a candidate value from a changed cell. */ +/*************************************************************/ +static void explain_markup_elim(grid *g, int chgd, int clue) +{ + int chgd_row, chgd_col, clue_row, clue_col; + + chgd_row = map[chgd].row+1; + chgd_col = map[chgd].col+1; + clue_row = map[clue].row+1; + clue_col = map[clue].col+1; + + explain_indent(solnfile); + fprintf(solnfile, "Candidate %s removed from row %d, col %d because of cell at row %d, col %d\n", + clues(g->cell[clue]), chgd_row, chgd_col, clue_row, clue_col); +} + +/*****************************************/ +/* Dump the state of the current markup. */ +/*****************************************/ +static void explain_current_markup(grid *g) +{ + if (g->exposed >= PUZZLE_CELLS) return; + + fprintf(solnfile, "\n"); + explain_indent(solnfile); + fprintf(solnfile, "Current markup is as follows:"); + diagnostic_grid(g, solnfile); + fprintf(solnfile, "\n"); +} + +/****************************************/ +/* Explain the solving of a given cell. */ +/****************************************/ +static void explain_solve_cell(grid *g, int chgd) +{ + int chgd_row, chgd_col; + + chgd_row = map[chgd].row+1; + chgd_col = map[chgd].col+1; + + explain_indent(solnfile); + fprintf(solnfile, "Cell at row %d, col %d solved with %s\n", + chgd_row, chgd_col, clues(g->cell[chgd])); +} + +/******************************************************************/ +/* Explain the current impasse reached during markup elimination. */ +/******************************************************************/ +static void explain_markup_impasse(grid *g, int chgd, int clue) +{ + int chgd_row, chgd_col, clue_row, clue_col; + + chgd_row = map[chgd].row+1; + chgd_col = map[chgd].col+1; + clue_row = map[clue].row+1; + clue_col = map[clue].col+1; + + explain_indent(solnfile); + fprintf(solnfile, "Impasse for cell at row %d, col %d because cell at row %d, col %d removes last candidate\n", + chgd_row, chgd_col, clue_row, clue_col); + explain_current_markup(g); +} + +/****************************************/ +/* Explain naked and/or hidden singles. */ +/****************************************/ +static void explain_singleton(grid *g, int chgd, int mask, char *vdesc) +{ + int chgd_row, chgd_col, chgd_reg; + + chgd_row = map[chgd].row+1; + chgd_col = map[chgd].col+1; + chgd_reg = map[chgd].region+1; + + explain_indent(solnfile); + fprintf(solnfile, "Cell of region %d at row %d, col %d will only solve for %s in this %s\n", + chgd_reg, chgd_row, chgd_col, clues(mask), vdesc); + explain_solve_cell(g, chgd); +} + +/*********************************/ +/* Explain initial puzzle state. */ +/*********************************/ +static void explain_markup() +{ + fprintf(solnfile, "\n"); + explain_indent(solnfile); + fprintf(solnfile, "Assume all cells may contain any values in the range: [1 - 9]\n"); +} + +/************************/ +/* Explain given clues. */ +/************************/ +static void explain_given(int cell, char val) +{ + int cell_row, cell_col; + + cell_row = map[cell].row+1; + cell_col = map[cell].col+1; + + explain_indent(solnfile); + fprintf(solnfile, "Cell at row %d, col %d is given clue value %c\n", cell_row, cell_col, val); +} + +/*******************************************/ +/* Explain region/row/column interactions. */ +/*******************************************/ +static void explain_vector_elim(char *desc, int i, int cell, int val, int region) +{ + int cell_row, cell_col; + + cell_row = map[cell].row+1; + cell_col = map[cell].col+1; + + explain_indent(solnfile); + fprintf(solnfile, "Candidate %s removed from cell at row %d, col %d because it aligns along %s %d in region %d\n", + clues(val), cell_row, cell_col, desc, i+1, region+1); +} + +/******************************************************************/ +/* Explain the current impasse reached during vector elimination. */ +/******************************************************************/ +static void explain_vector_impasse(grid *g, char *desc, int i, int cell, int val, int region) +{ + int cell_row, cell_col; + + cell_row = map[cell].row+1; + cell_col = map[cell].col+1; + + explain_indent(solnfile); + fprintf(solnfile, "Impasse at cell at row %d, col %d because candidate %s aligns along %s %d in region %d\n", + cell_row, cell_col, clues(val), desc, i+1, region+1); + explain_current_markup(g); +} + +/*****************************************************************/ +/* Explain the current impasse reached during tuple elimination. */ +/*****************************************************************/ +static void explain_tuple_impasse(grid *g, char *desc, int elt, int tuple, int count, int bits) +{ + explain_indent(solnfile); + fprintf(solnfile, "Impasse in %s %d because too many (%d) cells have %d-valued %s\n", + desc, elt+1, count, bits, clues(tuple)); + explain_current_markup(g); +} + +/*********************************************************************/ +/* Explain the removal of a tuple of candidate solutions from a cell */ +/*********************************************************************/ +static void explain_tuple_elim(char *desc, int elt, int tuple, int cell) +{ + explain_indent(solnfile); + fprintf(solnfile, "Values of %s in %s %d removed from cell at row %d, col %d\n", + clues(tuple), desc, elt+1, map[cell].row+1, map[cell].col+1); + +} + +/**************************************************/ +/* Indicate that a viable solution has been found */ +/**************************************************/ +static void explain_soln_found(grid *g) +{ + char buf[90]; + + fprintf(solnfile, "\n"); + explain_indent(solnfile); + fprintf(solnfile, "Solution found: %s\n", format_answer(g, buf)); + print_grid(buf, solnfile); + fprintf(solnfile, "\n"); +} + +/***************************/ +/* Show the initial puzzle */ +/***************************/ +static void explain_grid(grid *g) +{ + char buf[90]; + + fprintf(solnfile, "Initial puzzle: %s\n", format_answer(g, buf)); + print_grid(buf, solnfile); + explain_current_markup(g); + fprintf(solnfile, "\n"); +} + +/*************************************************/ +/* Explain attempt at a trial and error solution */ +/*************************************************/ +static void explain_trial(int cell, int value) +{ + explain_indent(solnfile); + fprintf(solnfile, "Attempt trial where cell at row %d, col %d is assigned value %s\n", + map[cell].row+1, map[cell].col+1, clues(value)); +} + +/**********************************************/ +/* Explain back out of current trial solution */ +/**********************************************/ +static void explain_backtrack() +{ + if (lvl <= 1) return; + + explain_indent(solnfile); + fprintf(solnfile, "Backtracking\n\n"); +} + +#define EXPLAIN_MARKUP if (explain) explain_markup() +#define EXPLAIN_CURRENT_MARKUP(g) if (explain) explain_current_markup((g)) +#define EXPLAIN_GIVEN(cell, val) if (explain) explain_given((cell), (val)) +#define EXPLAIN_MARKUP_ELIM(g, chgd, clue) if (explain) explain_markup_elim((g), (chgd), (clue)) +#define EXPLAIN_MARKUP_SOLVE(g, cell) if (explain) explain_solve_cell((g), (cell)) +#define EXPLAIN_MARKUP_IMPASSE(g, chgd, clue) if (explain) explain_markup_impasse((g), (chgd), (clue)) +#define EXPLAIN_SINGLETON(g, chgd, mask, vdesc) if (explain) explain_singleton((g), (chgd), (mask), (vdesc)) +#define EXPLAIN_VECTOR_ELIM(desc, i, cell, v, r) if (explain) explain_vector_elim((desc), (i), (cell), (v), (r)) +#define EXPLAIN_VECTOR_IMPASSE(g, desc, i, cell, v, r) if (explain) explain_vector_impasse((g), (desc), (i), (cell), (v), (r)) +#define EXPLAIN_VECTOR_SOLVE(g, cell) if (explain) explain_solve_cell((g), (cell)) +#define EXPLAIN_TUPLE_IMPASSE(g, desc, j, c, count, i) if (explain) explain_tuple_impasse((g), (desc), (j), (c), (count), (i)) +#define EXPLAIN_TUPLE_ELIM(desc, j, c, cell) if (explain) explain_tuple_elim((desc), (j), (c), (cell)) +#define EXPLAIN_TUPLE_SOLVE(g, cell) if (explain) explain_solve_cell((g), (cell)) +#define EXPLAIN_SOLN_FOUND(g) if (explain) explain_soln_found((g)); +#define EXPLAIN_GRID(g) if (explain) explain_grid((g)); +#define EXPLAIN_TRIAL(cell, val) if (explain) explain_trial((cell), (val)); +#define EXPLAIN_BACKTRACK if (explain) explain_backtrack(); +#define EXPLAIN_INDENT(h) if (explain) explain_indent((h)) + +#else + +#define EXPLAIN_MARKUP +#define EXPLAIN_CURRENT_MARKUP(g) +#define EXPLAIN_GIVEN(cell, val) +#define EXPLAIN_MARKUP_ELIM(g, chgd, clue) +#define EXPLAIN_MARKUP_SOLVE(g, cell) +#define EXPLAIN_MARKUP_IMPASSE(g, chgd, clue) +#define EXPLAIN_SINGLETON(g, chgd, mask, vdesc); +#define EXPLAIN_VECTOR_ELIM(desc, i, cell, v, r) +#define EXPLAIN_VECTOR_IMPASSE(g, desc, i, cell, v, r) +#define EXPLAIN_VECTOR_SOLVE(g, cell) +#define EXPLAIN_TUPLE_IMPASSE(g, desc, j, c, count, i) +#define EXPLAIN_TUPLE_ELIM(desc, j, c, cell) +#define EXPLAIN_TUPLE_SOLVE(g, cell) +#define EXPLAIN_SOLN_FOUND(g) +#define EXPLAIN_GRID(g) +#define EXPLAIN_TRIAL(cell, val) +#define EXPLAIN_BACKTRACK +#define EXPLAIN_INDENT(h) + +#endif + + +/*****************************************************/ +/* Initialize a grid to an empty state. */ +/* At the start, all cells can have any value */ +/* so set all 9 lower order bits in each cell. */ +/* In effect, the 9x9 grid now has markup that */ +/* specifies that each cell can assume any value */ +/* of 1 through 9. */ +/*****************************************************/ + +static void init_grid(grid *g) +{ + int i; + + for (i = 0; i < PUZZLE_CELLS; i++) g->cell[i] = 0x01ff; + memset(g->cellflags, 0, PUZZLE_CELLS*sizeof(g->cellflags[0])); + g->exposed = 0; + g->givens = 0; + g->inc = 0; + g->maxlvl = 0; + g->score = 0; + g->solncount = 0; + g->reward = 1; + g->next = NULL; + g->tail = 0; + EXPLAIN_MARKUP; +} + +/*****************************************************/ +/* Convert a puzzle from the input format, */ +/* i.e. a string of 81 non-blank characters */ +/* with ASCII digits '1' thru '9' specified */ +/* for the givens, and non-numeric characters */ +/* for the remaining cells. The string, read */ +/* left-to-right fills the 9x9 Sudoku grid */ +/* in left-to-right, top-to-bottom order. */ +/*****************************************************/ + +static void cvt_to_grid(grid *g, char *game) +{ + int i; + + init_grid(g); + + for (i = 0; i < PUZZLE_CELLS; i++) { + if (is_given(game[i])) { + /* warning -- ASCII charset assumed */ + g->cell[i] = 1 << (game[i] - '1'); + g->cellflags[i] = GIVEN; + g->givens += 1; + g->solved[g->exposed++] = i; + EXPLAIN_GIVEN(i, game[i]); + } + } + EXPLAIN_GRID(g); +} + +/****************************************************************/ +/* Print the partially solved puzzle and all associated markup */ +/* in 9x9 fashion. */ +/****************************************************************/ + +static void diagnostic_grid(grid *g, FILE *h) +{ + int i, j, flag; + short c; + char line1[40], line2[40], line3[40], cbuf1[5], cbuf2[5], cbuf3[5], outbuf[PUZZLE_CELLS+1]; + + /* Sanity check */ + for (flag = 1, i = 0; flag && i < PUZZLE_CELLS; i++) { + if (bitcount(g->cell[i]) != 1) { + flag = 0; + } + } + + /* Don't need to print grid with diagnostic markup? */ + if (flag) { + format_answer(g, outbuf); + print_grid(outbuf, h); + fflush(h); + return; + } + + strcpy(cbuf1, " |"); + strcpy(cbuf2, cbuf1); + strcpy(cbuf3, cbuf1); + fprintf(h, "\n"); + + for (i = 0; i < PUZZLE_DIM; i++) { + + *line1 = *line2 = *line3 = 0; + + for (j = 0; j < PUZZLE_DIM; j++) { + + c = g->cell[row[i][j]]; + + if (bitcount(c) == 1) { + strcpy(cbuf1, " |"); + strcpy(cbuf2, cbuf1); + strcpy(cbuf3, cbuf1); + cbuf2[1] = symtab[c]; + } + else { + if (c & 1) cbuf1[0] = '*'; else cbuf1[0] = '.'; + if (c & 2) cbuf1[1] = '*'; else cbuf1[1] = '.'; + if (c & 4) cbuf1[2] = '*'; else cbuf1[2] = '.'; + if (c & 8) cbuf2[0] = '*'; else cbuf2[0] = '.'; + if (c & 16) cbuf2[1] = '*'; else cbuf2[1] = '.'; + if (c & 32) cbuf2[2] = '*'; else cbuf2[2] = '.'; + if (c & 64) cbuf3[0] = '*'; else cbuf3[0] = '.'; + if (c & 128) cbuf3[1] = '*'; else cbuf3[1] = '.'; + if (c & 256) cbuf3[2] = '*'; else cbuf3[2] = '.'; + } + + strcat(line1, cbuf1); + strcat(line2, cbuf2); + strcat(line3, cbuf3); + } + + EXPLAIN_INDENT(h); + fprintf(h, "+---+---+---+---+---+---+---+---+---+\n"); + EXPLAIN_INDENT(h); + fprintf(h, "|%s\n", line1); + EXPLAIN_INDENT(h); + fprintf(h, "|%s\n", line2); + EXPLAIN_INDENT(h); + fprintf(h, "|%s\n", line3); + } + EXPLAIN_INDENT(h); + fprintf(h, "+---+---+---+---+---+---+---+---+---+\n"); fflush(h); +} + +/***********************************************************************/ +/* Validate that a sudoku grid contains a valid solution. Return 1 if */ +/* true, 0 if false. If the verbose argument is non-zero, then print */ +/* reasons for invalidating the solution to stderr. */ +/***********************************************************************/ + +static int validate(grid *g, int verbose) +{ + int i, j, regmask, rowmask, colmask, flag = 1; + + /* Sanity check */ + for (i = 0; i < PUZZLE_CELLS; i++) { + if (bitcount(g->cell[i]) != 1) { + if (verbose) { + fprintf(rejects, "Cell %d at row %d, col %d has no unique soln.\n", 1+i, 1+map[i].row, 1+map[i].col); fflush(rejects); + flag = 0; + } else return 0; + } + } + + /* Check rows */ + for (i = 0; i < PUZZLE_DIM; i++) { + for (rowmask = j = 0; j < PUZZLE_DIM; j++) { + if (bitcount(g->cell[row[i][j]]) == 1) rowmask |= g->cell[row[i][j]]; + } + if (rowmask != 0x01ff) { + if (verbose) { + fprintf(rejects, "Row %d is inconsistent.\n", 1+i); fflush(rejects); + flag = 0; + } else return 0; + } + } + + /* Check columns */ + for (i = 0; i < PUZZLE_DIM; i++) { + for (colmask = j = 0; j < PUZZLE_DIM; j++) { + if (bitcount(g->cell[col[i][j]]) == 1) colmask |= g->cell[col[i][j]]; + } + if (colmask != 0x01ff) { + if (verbose) { + fprintf(rejects, "Column %d is inconsistent.\n", 1+i); fflush(rejects); + flag = 0; + } else return 0; + } + } + + /* Check 3x3 regions */ + for (i = 0; i < PUZZLE_DIM; i++) { + for (regmask = j = 0; j < PUZZLE_DIM; j++) { + if (bitcount(g->cell[region[i][j]]) == 1) regmask |= g->cell[region[i][j]]; + } + if (regmask != 0x01ff) { + if (verbose) { + fprintf(rejects, "Region %d is inconsistent.\n", 1+i); fflush(rejects); + flag = 0; + } else return 0; + } + } + + return flag; +} + +/********************************************************************************/ +/* This function uses the cells with unique values, i.e. the given */ +/* or subsequently discovered solution values, to eliminate said values */ +/* as candidates in other as yet unsolved cells in the associated */ +/* rows, columns, and 3x3 regions. */ +/* */ +/* The function has three possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified, and */ +/* STUCK - Markup results are invalid, i.e. a cell has no candidate values */ +/********************************************************************************/ + +static int mark_cells(grid *g) +{ + int i, chgflag, bc; + int const *r, *c, *reg; + short elt, mask, before; + + + chgflag = NOCHANGE; + + while (g->tail < g->exposed) { + + elt = g->solved[g->tail++]; + + r = row[map[elt].row]; + c = col[map[elt].col]; + reg = region[map[elt].region]; + + mask = ~g->cell[elt]; + + for (i = 0; i < PUZZLE_DIM; i++) { + + if (r[i] != elt) { + + /* Get the cell value */ + before = g->cell[r[i]]; + + /* Eliminate this candidate value whilst preserving other candidate values */ + g->cell[r[i]] &= mask; + + /* Did the cell change value? */ + if (before != g->cell[r[i]]) { + + chgflag |= CHANGE; /* Flag that puzzle markup was changed */ + g->score += g->inc; /* More work means higher scoring */ + + if (!(bc = bitcount(g->cell[r[i]]))) { + EXPLAIN_MARKUP_IMPASSE(g, r[i], elt); + return STUCK; /* Crap out if no candidates remain */ + } + + EXPLAIN_MARKUP_ELIM(g, r[i], elt); + + /* Check if we solved for this cell, i.e. bit count indicates a unique value */ + if (bc == 1) { + g->cellflags[r[i]] = FOUND; /* Mark cell as found */ + g->score += g->reward; /* Add to puzzle score */ + g->solved[g->exposed++] = r[i]; + EXPLAIN_MARKUP_SOLVE(g, r[i]); + } + } + } + + if (c[i] != elt) { + + /* Get the cell value */ + before = g->cell[c[i]]; + + /* Eliminate this candidate value whilst preserving other candidate values */ + g->cell[c[i]] &= mask; + + /* Did the cell change value? */ + if (before != g->cell[c[i]]) { + + chgflag |= CHANGE; /* Flag that puzzle markup was changed */ + g->score += g->inc; /* More work means higher scoring */ + + if (!(bc = bitcount(g->cell[c[i]]))) { + EXPLAIN_MARKUP_IMPASSE(g, c[i], elt); + return STUCK; /* Crap out if no candidates remain */ + } + + EXPLAIN_MARKUP_ELIM(g, c[i], elt); + + /* Check if we solved for this cell, i.e. bit count indicates a unique value */ + if (bc == 1) { + g->cellflags[c[i]] = FOUND; /* Mark cell as found */ + g->score += g->reward; /* Add to puzzle score */ + g->solved[g->exposed++] = c[i]; + EXPLAIN_MARKUP_SOLVE(g, c[i]); + } + } + } + + if (reg[i] != elt) { + + /* Get the cell value */ + before = g->cell[reg[i]]; + + /* Eliminate this candidate value whilst preserving other candidate values */ + g->cell[reg[i]] &= mask; + + /* Did the cell change value? */ + if (before != g->cell[reg[i]]) { + + chgflag |= CHANGE; /* Flag that puzzle markup was changed */ + g->score += g->inc; /* More work means higher scoring */ + + if (!(bc = bitcount(g->cell[reg[i]]))) { + EXPLAIN_MARKUP_IMPASSE(g, reg[i], elt); + return STUCK; /* Crap out if no candidates remain */ + } + + EXPLAIN_MARKUP_ELIM(g, reg[i], elt); + + /* Check if we solved for this cell, i.e. bit count indicates a unique value */ + if (bc == 1) { + g->cellflags[reg[i]] = FOUND; /* Mark cell as found */ + g->score += g->reward; /* Add to puzzle score */ + g->solved[g->exposed++] = reg[i]; + EXPLAIN_MARKUP_SOLVE(g, reg[i]); + } + } + } + + } + } + + return chgflag; +} + + +/*******************************************************************/ +/* Identify and "solve" all cells that, by reason of their markup, */ +/* can only assume one specific value, i.e. the cell is the only */ +/* one in a row/column/region (specified by vector) that is */ +/* able to assume a particular value. */ +/* */ +/* The function has two possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified. */ +/*******************************************************************/ + +static int find_singletons(grid *g, int const *vector, char *vdesc) +{ + int i, j, mask, hist[PUZZLE_DIM], value[PUZZLE_DIM], found = NOCHANGE; + + /* We are going to create a histogram of cell candidate values */ + /* for the specified cell vector (row/column/region). */ + /* First set all buckets to zero. */ + memset(hist, 0, sizeof(hist[0])*PUZZLE_DIM); + + /* For each cell in the vector... */ + for (i = 0; i < PUZZLE_DIM; i++) { + + /* For each possible candidate value... */ + for (mask = 1, j = 0; j < PUZZLE_DIM; j++) { + + /* If the cell may possibly assume this value... */ + if (g->cell[vector[i]] & mask) { + + value[j] = vector[i]; /* Save the cell coordinate */ + hist[j] += 1; /* Bump bucket in histogram */ + } + + mask <<= 1; /* Next candidate value */ + } + } + + /* Examine each bucket in the histogram... */ + for (mask = 1, i = 0; i < PUZZLE_DIM; i++) { + + /* If the bucket == 1 and the cell is not already solved, */ + /* then the cell has a unique solution specified by "mask" */ + if (hist[i] == 1 && !g->cellflags[value[i]]) { + + found = CHANGE; /* Indicate that markup has been changed */ + g->cell[value[i]] = mask; /* Assign solution value to cell */ + g->cellflags[value[i]] = FOUND; /* Mark cell as solved */ + g->score += g->reward; /* Bump puzzle score */ + g->solved[g->exposed++] = value[i]; + EXPLAIN_SINGLETON(g, value[i], mask, vdesc); + } + + mask <<= 1; /* Get next candidate value */ + } + + return found; +} + + +/*******************************************************************/ +/* Find all cells with unique solutions (according to markup) */ +/* and mark them as found. Do this for each row, column, and */ +/* region. */ +/* */ +/* The function has two possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified. */ +/*******************************************************************/ + +static int eliminate_singles(grid *g) +{ + int i, found = NOCHANGE; + + /* Do rows */ + for (i = 0; i < PUZZLE_DIM; i++) { + found |= find_singletons(g, row[i], (char *)"row"); + } + + /* Do columns */ + for (i = 0; i < PUZZLE_DIM; i++) { + found |= find_singletons(g, col[i], (char *)"column"); + } + + /* Do regions */ + for (i = 0; i < PUZZLE_DIM; i++) { + found |= find_singletons(g, region[i], (char *)"region"); + } + + return found; +} + +/********************************************************************************/ +/* Solves simple puzzles, i.e. single elimination */ +/* */ +/* The function has three possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified, and */ +/* STUCK - Markup results are invalid, i.e. a cell has no candidate values */ +/********************************************************************************/ +static int simple_solver(grid *g) +{ + int flag = NOCHANGE; + + /* Mark the unsolved cells with candidate solutions based upon the current set of "givens" and solved cells */ + while ((flag |= mark_cells(g)) == CHANGE) { + + g->inc = 1; /* After initial markup, we start scoring for additional markup work */ + + EXPLAIN_CURRENT_MARKUP(g); + + /* Continue to eliminate cells with unique candidate solutions from the game until */ + /* elimination and repeated markup efforts produce no changes in the remaining */ + /* candidate solutions. */ + if (eliminate_singles(g) == NOCHANGE) break; + + EXPLAIN_CURRENT_MARKUP(g); + } + + return flag; +} + +/************************************************************************************/ +/* Test a region to see if the candidate solutions for a paticular number */ +/* are confined to one row or column, and if so, eliminate */ +/* their occurences in the remainder of the given row or column. */ +/* */ +/* The function has three possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified, and */ +/* STUCK - Markup results are invalid, i.e. a cell has no candidate values */ +/************************************************************************************/ + +static int region_vector_elim(grid *g, int region_no, int num) +{ + int i, j, r, c, mask, t, found; + short rowhist[PUZZLE_DIM], colhist[PUZZLE_DIM]; + + /* Init */ + found = NOCHANGE; + memset(rowhist, 0, sizeof(rowhist[0])*PUZZLE_DIM); + memset(colhist, 0, sizeof(colhist[0])*PUZZLE_DIM); + + mask = 1 << num; + + /* Create histograms for row and column placements for the value being checked */ + for (i = 0; i < PUZZLE_DIM; i++) { + j = region[region_no][i]; + if ((g->cell[j] & mask)) { + rowhist[map[j].row] += 1; + colhist[map[j].col] += 1; + } + } + + /* Figure out if this number lies in only one row or column */ + + /* Check rows first*/ + r = c = -1; + for (i = 0; i < PUZZLE_DIM; i++) { + if (rowhist[i]) { + if (r < 0) { + r = i; + } + else { + r = -1; + break; + } + } + } + + /* Now check columns */ + for (i = 0; i < PUZZLE_DIM; i++) { + if (colhist[i]) { + if (c < 0) { + c = i; + } + else { + c = -1; + break; + } + } + } + + /* If the number is only in one row, then eliminate this number from the cells in the row outside of this region */ + if (r >= 0) { + for (i = 0; i < PUZZLE_DIM; i++) { + j = row[r][i]; + if (map[j].region != region_no && !g->cellflags[j]) { + t = g->cell[j]; + if ((g->cell[j] &= ~mask) == 0) { + EXPLAIN_VECTOR_IMPASSE(g, "row", r, j, mask, region_no); + g->score += 10; + return STUCK; + } + if (t != g->cell[j]) { + found = CHANGE; + g->score += g->inc; + EXPLAIN_VECTOR_ELIM("row", r, j, mask, region_no); + if (bitcount(g->cell[j]) == 1) { + g->cellflags[j] = FOUND; + g->score += g->reward; + g->solved[g->exposed++] = j; + EXPLAIN_VECTOR_SOLVE(g, j); + } + } + } + } + } + + /* If the number is only in one column, then eliminate this number from the cells in the column outside of this region */ + else if (c >= 0) { + for (i = 0; i < PUZZLE_DIM; i++) { + j = col[c][i]; + if (map[j].region != region_no && !g->cellflags[j]) { + t = g->cell[j]; + if ((g->cell[j] &= ~mask) == 0) { + EXPLAIN_VECTOR_IMPASSE(g, "column", c, j, mask, region_no); + g->score += 10; + return STUCK; + } + if (t != g->cell[j]) { + found = CHANGE; + g->score += g->inc; + EXPLAIN_VECTOR_ELIM("column", c, j, mask, region_no); + if (bitcount(g->cell[j]) == 1) { + g->cellflags[j] = FOUND; + g->score += g->reward; + g->solved[g->exposed++] = j; + EXPLAIN_VECTOR_SOLVE(g, j); + } + } + } + } + } + + if (found == CHANGE) { + g->score += 10; /* Bump score for sucessfully invoking this rule */ + } + + return found; +} + +/**********************************************************************************/ +/* Test all regions to see if the possibilities for a number */ +/* are confined to specific rows or columns, and if so, eliminate */ +/* the occurence of candidate solutions from the remainder of the */ +/* specified row or column. */ +/* */ +/* The function has three possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified, and */ +/* STUCK - Markup results are invalid, i.e. a cell has no candidate values */ +/**********************************************************************************/ + +static int vector_elimination(grid *g) +{ + int i, j, rc; + + /* For each region... */ + for (rc = NOCHANGE, i = 0; i < PUZZLE_DIM && rc != STUCK; i++) { + + /* For each digit... */ + for (j = 0; j < PUZZLE_DIM && rc != STUCK; j++) { + + /* Eliminate candidates outside of regions when a particular */ + /* candidate value aligns itself to a row or column within */ + /* a 3x3 region. */ + rc |= region_vector_elim(g, i, j); + } + } + + return rc; +} + +/**********************************************************************************/ +/* This function implements the rule that when a subset of cells */ +/* in a row/column/region contain matching subsets of candidate */ +/* solutions, i.e. 2 matching possibilities for 2 cells, 3 */ +/* matching possibilities for 3 cells, etc., then those */ +/* candidates may be eliminated from the other cells in the */ +/* row, column, or region. */ +/* */ +/* The function has three possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified, and */ +/* STUCK - Markup results are invalid, i.e. a cell has no candidate values */ +/**********************************************************************************/ + +static int elim_matches(grid *g, int const *cell_list, char *desc, int ndx) +{ + int i, j, k, e, count, rc, flag; + short c, mask, tmp, elts[PUZZLE_DIM], eliminated[PUZZLE_DIM]; + static int counts[1<cell[k]; /* Copy original cell candidates */ + + if (bitcount(g->cell[k]) == i) { + counts[g->cell[k]] += 1; /* The bucket records the number of cells with this subset */ + } + } + + /* For each cell in the list... */ + for (e = j = 0; j < PUZZLE_DIM; j++) { + + c = g->cell[cell_list[j]]; /* Get cell's candidates */ + + /* Check to see if we've already eliminated this subset */ + for (k = 0; k < e; k++) + if (c == eliminated[k]) break; + if (e && k < e) continue; + + /* Get count from histogram bucket */ + count = (int) (counts[c]); + + /* If too few solution candidates for the number of cells, then we're stuck */ + if (count > i) { + EXPLAIN_TUPLE_IMPASSE(g, desc, ndx, c, count, i); + /* Clean up static array */ + for (k = 0; k < 9; k++) counts[elts[k]] = 0; + g->score += 10; + return STUCK; + } + + /* Do candidate and cell counts match? */ + if (count == i) { + + /* Compute mask used to eliminate candidates from other cells */ + mask = ~c; + + /* Record (for later) the values being eliminated */ + eliminated[e++] = c; + + /* Eliminate candidates from the other cells in the list */ + + /* For each cell... */ + for (k = 0; k < PUZZLE_DIM; k++) { + + /* If the cell candidates do not exactly match the current subset... */ + if (c != g->cell[cell_list[k]] && !g->cellflags[cell_list[k]]) { + + /* Get cell candidates */ + tmp = g->cell[cell_list[k]]; + + /* Eliminate candidates with our mask */ + g->cell[cell_list[k]] &= mask; + + /* Did the elimination change the candidates? */ + if (tmp != g->cell[cell_list[k]]) { + + /* Note the change and bump the score */ + flag = CHANGE; + g->score += i; + + EXPLAIN_TUPLE_ELIM(desc, ndx, c, cell_list[k]); + + /* Did we solve the cell under consideration? */ + if (bitcount(g->cell[cell_list[k]]) == 1) { + + /* Mark cell as found and bump the score */ + g->cellflags[cell_list[k]] = FOUND; + g->score += g->reward; + g->solved[g->exposed++] = cell_list[k]; + EXPLAIN_TUPLE_SOLVE(g, cell_list[k]); + } + } + } + } + } + } + + /* Cleanup the static histogram array */ + for (j = 0; j < PUZZLE_DIM; j++) counts[elts[j]] = 0; + + rc |= flag; + } + + return rc; +} + +/**********************************************************************************/ +/* Eliminate subsets from rows, columns, and regions. */ +/* */ +/* The function has three possible return values: */ +/* NOCHANGE - Markup did not change during the last pass, */ +/* CHANGE - Markup was modified, and */ +/* STUCK - Markup results are invalid, i.e. a cell has no candidate values */ +/**********************************************************************************/ + +static int mult_elimination(grid *g) +{ + int i, rc = NOCHANGE; + + /* Eliminate subsets from rows */ + for (i = 0; i < PUZZLE_DIM; i++) { + rc |= elim_matches(g, row[i], (char *)"row", i); + } + + /* Eliminate subsets from columns */ + for (i = 0; i < PUZZLE_DIM; i++) { + rc |= elim_matches(g, col[i], (char *)"column", i); + } + + /* Eliminate subsets from regions */ + for (i = 0; i < PUZZLE_DIM; i++) { + rc |= elim_matches(g, region[i], (char *)"region", i); + } + + return rc; +} + +/**************************************************/ +/* Entry point to the recursive solver algorithm. */ +/**************************************************/ +static int rsolve(grid *g, return_soln soln_callback) +{ + int i, j, min, c, weight, mask, flag = 0; + grid mygrid; + + /* Keep track of recursive depth */ + lvl += 1; + if (lvl > g->maxlvl) g->maxlvl = lvl; + + for (;;) { + + /* Attempt a simple solution */ + if (simple_solver(g) == STUCK) break; + + /* Check for solution */ + if (g->exposed >= PUZZLE_CELLS) break; + + g->reward += 2; /* Bump reward as we graduate to more "advanced" solving techniques */ + + /* Eliminate tuples */ + if ((flag = mult_elimination(g)) == CHANGE) { + EXPLAIN_CURRENT_MARKUP(g); + continue; + } + + /* Check if impasse */ + if (flag == STUCK) break; + + /* Check for solution */ + if (g->exposed >= PUZZLE_CELLS) break; + + /* Eliminate clues aligned within regions from exterior cells in rows or columns */ + if ((flag = vector_elimination(g)) == CHANGE) { + EXPLAIN_CURRENT_MARKUP(g); + continue; + } + + /* Check if impasse */ + if (flag == STUCK) break; + + /* Check for solution */ + if (g->exposed >= PUZZLE_CELLS) break; + + g->reward += 5; /* Bump reward as we are about to start trial soutions */ + + /* Attempt a trial solution */ + memcpy(&mygrid, g, sizeof(grid)); /* Make working copy of puzzle */ + + /* Find the first cell with the smallest number of alternatives */ + for (weight= 0, c = -1, min = PUZZLE_DIM, i = 0; i < PUZZLE_CELLS; i++) { + if (!mygrid.cellflags[i]) { + j = bitcount(mygrid.cell[i]); + weight += 1; + if (j < min) { + min = j; + c = i; + } + } + } + + mygrid.score += weight; /* Add penalty to score */ + + /* Cell at index 'c' will be our starting point */ + if (c >= 0) for (mask = 1, i = 0; i < PUZZLE_DIM; i++) { + + /* Is this a candidate? */ + if (mask & g->cell[c]) { + + EXPLAIN_TRIAL(c, mask); + + mygrid.score += (int)(((50.0 * lvl * weight) / (double)(PUZZLE_CELLS)) + 0.5); /* Add'l penalty */ + + /* Try one of the possible candidates for this cell */ + mygrid.cell[c] = mask; + mygrid.cellflags[c] = FOUND; + mygrid.solved[mygrid.exposed++] = c; + + EXPLAIN_CURRENT_MARKUP(&mygrid); + flag = rsolve(&mygrid, soln_callback); /* Recurse with working copy of puzzle */ + + /* Did we find a solution? */ + if (flag == FOUND && !enumerate_all) { + EXPLAIN_BACKTRACK; + lvl -= 1; + return FOUND; + } + + /* Preserve score, solution count and recursive depth as we back out of recursion */ + g->score = mygrid.score; + g->solncount = mygrid.solncount; + g->maxlvl = mygrid.maxlvl; + memcpy(&mygrid, g, sizeof(grid)); + } + mask <<= 1; /* Get next possible candidate */ + } + + break; + } + + if (g->exposed == PUZZLE_CELLS && validate(g, 0)) { + soln_callback(g); + g->solncount += 1; + EXPLAIN_SOLN_FOUND(g); + EXPLAIN_BACKTRACK; + lvl -= 1; + flag = FOUND; + } else { + EXPLAIN_BACKTRACK; + lvl -= 1; + flag = STUCK; + if (!lvl && !g->solncount) validate(g, 1); /* Print verbose diagnostic for insoluble puzzle */ + } + + return flag; +} + +/*****************************************************************/ +/* Add a puzzle solution to the singly linked list of solutions. */ +/* Crap out if no memory available. */ +/*****************************************************************/ + +static int add_soln(grid *g) +{ + grid *tmp; + + if ((tmp = (grid *)malloc(sizeof(grid))) == NULL) { + fprintf(stderr, "Out of memory.\n"); + exit(1); + } + memcpy(tmp, g, sizeof(grid)); + tmp->next = soln_list; + soln_list = tmp; + return 0; +} + +/************************************/ +/* Print hints as to command usage. */ +/************************************/ + +static void usage() +{ + fprintf(stderr, "Usage:\n\t%s {-p puzzle | -f } [-o ]\n", myname); + fprintf(stderr, "\t\t[-r ] [-1][-a][-c][-G][-g][-l][-m][-n][-s]\n"); + fprintf(stderr, "where:\n\t-1\tSearch for first solution, otherwise all solutions are returned\n" + "\t-a\tRequests that the answer (solution) be printed\n" + "\t-c\tPrint a count of solutions for each puzzle\n" + "\t-d\tPrint the recursive trial depth required to solve the puzzle\n" +#ifdef EXPLAIN + "\t-e\tPrint a step-by-step explanation of the solution(s)\n" +#endif + "\t-f\tTakes an argument which specifes an input file\n\t\tcontaining one or more unsolved puzzles (default: stdin)\n" + "\t-G\tPrint the puzzle solution(s) in a 9x9 grid format\n" + "\t-g\tPrint the number of given clues\n" + "\t-m\tPrint an octal mask for the puzzle givens\n" + "\t-n\tNumber each result\n" + "\t-o\tSpecifies an output file for the solutions (default: stdout)\n" + "\t-p\tTakes an argument giving a single inline puzzle to be solved\n" + "\t-r\tSpecifies an output file for unsolvable puzzles\n\t\t(default: stderr)\n" + "\t-s\tPrint the puzzle's score or difficulty rating\n" + "\t-?\tPrint usage information\n\n"); + fprintf(stderr, "The return code is zero if all puzzles had unique solutions,\n" + "(or have one or more solutions when -1 is specified) and non-zero\n" + "when no unique solution exists.\n"); +} + +/********************************************************/ +/* Print the puzzle as an 81 character string of digits */ +/********************************************************/ + +static char *format_answer(grid *g, char *outbuf) +{ + int i; + + for (i = 0; i < PUZZLE_CELLS; i++) + outbuf[i] = symtab[g->cell[i]]; + outbuf[i] = 0; + + return outbuf; +} + +/*******************************************/ +/* Print the puzzle as a standard 9x9 grid */ +/*******************************************/ + +static void print_grid(char *sud, FILE *h) +{ + + fprintf(h, "\n"); + EXPLAIN_INDENT(h); + fprintf(h, "+---+---+---+\n"); + + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud, PUZZLE_ORDER, PUZZLE_ORDER, sud+3, PUZZLE_ORDER, PUZZLE_ORDER, sud+6); + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+9, PUZZLE_ORDER, PUZZLE_ORDER, sud+12, PUZZLE_ORDER, PUZZLE_ORDER, sud+15); + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+18, PUZZLE_ORDER, PUZZLE_ORDER, sud+21, PUZZLE_ORDER, PUZZLE_ORDER, sud+24); + + EXPLAIN_INDENT(h); + fprintf(h, "+---+---+---+\n"); + + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+27, PUZZLE_ORDER, PUZZLE_ORDER, sud+30, PUZZLE_ORDER, PUZZLE_ORDER, sud+33); + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+36, PUZZLE_ORDER, PUZZLE_ORDER, sud+39, PUZZLE_ORDER, PUZZLE_ORDER, sud+42); + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+45, PUZZLE_ORDER, PUZZLE_ORDER, sud+48, PUZZLE_ORDER, PUZZLE_ORDER, sud+51); + + EXPLAIN_INDENT(h); + fprintf(h, "+---+---+---+\n"); + + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+54, PUZZLE_ORDER, PUZZLE_ORDER, sud+57, PUZZLE_ORDER, PUZZLE_ORDER, sud+60); + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+63, PUZZLE_ORDER, PUZZLE_ORDER, sud+66, PUZZLE_ORDER, PUZZLE_ORDER, sud+69); + EXPLAIN_INDENT(h); + fprintf(h, "|%*.*s|%*.*s|%*.*s|\n", PUZZLE_ORDER, PUZZLE_ORDER, sud+72, PUZZLE_ORDER, PUZZLE_ORDER, sud+75, PUZZLE_ORDER, PUZZLE_ORDER, sud+78); + + EXPLAIN_INDENT(h); + fprintf(h, "+---+---+---+\n"); +} + +/*****************************************************/ +/* Based upon the Left-to-Right-Top-to-Bottom puzzle */ +/* presented in "sbuf", create a 27 octal digit */ +/* mask of the givens in the 28 character buffer */ +/* pointed to by "mbuf." Return a pointer to mbuf. */ +/*****************************************************/ + +static char *cvt_to_mask(char *mbuf, char *sbuf) +{ + char *mask_buf = mbuf; + static const char *maskchar = "01234567"; + int i, m; + + mask_buf[PUZZLE_DIM*3] = 0; + for (i = 0; i < PUZZLE_CELLS; i += 3) { + m = 0; + if (is_given(sbuf[i])) { + m |= 4; + } + else { + sbuf[i] = '0'; + } + if (is_given(sbuf[i+1])) { + m |= 2; + } + else { + sbuf[i+1] = '0'; + } + if (is_given(sbuf[i+2])) { + m |= 1; + } + else { + sbuf[i+2] = '0'; + } + *mask_buf++ = maskchar[m]; + } + return mbuf; +} + +/*******************/ +/* Mainline logic. */ +/*******************/ + +int dupree_solver(int32_t dispflag,int32_t *scorep,char *puzzle) +{ + int argc; char *argv[4]; + int i, rc, bog, count, solved, unsolved, solncount=0, flag, prt_count, prt_num, prt_score, prt_answer, prt_depth, prt_grid, prt_mask, prt_givens, prt, len; + char *infile=0, *outfile=0, *rejectfile=0, inbuf[128], outbuf[128], mbuf[28]; + grid g, *s=0; + FILE *h=0; + soln_list = NULL; + myname = (char *)"internal"; + /* Get our command name from invoking command line + if ((myname = strrchr(argv[0], '/')) == NULL) + myname = argv[0]; + else + myname++; + argc = 3; + argv[1] = "-p"; + argv[2] = puzzle; + argv[3] = 0;*/ + /* Print sign-on message to console */ + //fprintf(stderr, "%s version %s\n", myname, VERSION); fflush(stderr); + argc = 1; + /* Init */ + h = 0;//stdin; + solnfile = stdout; + rejects = stderr; + rejectfile = infile = outfile = NULL; + rc = bog = prt_mask = prt_grid = prt_score = prt_depth = prt_answer = prt_count = prt_num = prt_givens = 0; + *inbuf = 0; +#ifdef skip + /* Parse command line options */ + while ((opt = getopt(argc, argv, OPTIONS)) != -1) { + switch (opt) { + case '1': + enumerate_all = 0; /* only find first soln */ + break; + case 'a': + prt_answer = 1; /* print solution */ + break; + case 'c': + prt_count = 1; /* number solutions */ + break; + case 'd': + prt_depth = 1; + break; +#ifdef EXPLAIN + case 'e': + explain = 1; + break; +#endif + case 'f': + if (*inbuf) { // -p and -f options are mutually exclusive + fprintf(stderr, "The -p and -f options are mutually exclusive\n"); + usage(); + exit(1); + } + infile = optarg; // get name of input file + break; + case 'G': + prt_grid = 1; + break; + case 'g': + prt_givens = 1; + break; + case 'm': + prt_mask = 1; + break; + case 'n': + prt_num = 1; + break; + case 'o': + outfile = optarg; + break; + case 'p': + if (infile) { + fprintf(stderr, "The -p and -f options are mutually exclusive\n"); + usage(); + exit(1); + } + if (strlen(optarg) == PUZZLE_CELLS) { + strcpy(inbuf, optarg); + } + else { + fprintf(stderr, "Invalid puzzle specified: %s\n", optarg); + usage(); + exit(1); + } + h = NULL; + break; + case 'r': + rejectfile = optarg; + break; + case 's': + prt_score = 1; + break; + default: + case '?': + usage(); + exit(1); + } + } + /* Anthing else on the command line is bogus */ + if (argc > optind) { + fprintf(stderr, "Extraneous args: "); + for (i = optind; i < argc; i++) { + fprintf(stderr, "%s ", argv[i]); + } + fprintf(stderr, "\n\n"); + usage(); + exit(1); + } + + if (!enumerate_all && prt_score) { + fprintf(stderr, "Scoring is meaningless when multi-solution mode is disabled.\n"); + } + + if (rejectfile && !(rejects = fopen(rejectfile, "w"))) { + fprintf(stderr, "Failed to open reject output file: %s\n", rejectfile); + exit(1); + } + + if (outfile && !(solnfile = fopen(outfile, "w"))) { + fprintf(stderr, "Failed to open solution output file: %s\n", outfile); + exit(1); + } + + /*if (infile && strcmp(infile, "-") && !(h = fopen(infile, "r"))) { + fprintf(stderr, "Failed to open input game file: %s\n", infile); + exit(1); + } + if (h) fgets(inbuf, 128, h);*/ +#endif + prt_answer = dispflag; /* print solution */ + //prt_count = dispflag; /* number solutions */ + prt_score = dispflag; + prt_givens = dispflag; + prt_num = dispflag; + /* Set prt flag if we're printing anything at all */ + prt = prt_mask | prt_grid | prt_score | prt_depth | prt_answer | prt_num | prt_givens; + + strcpy(inbuf,puzzle); + count = solved = unsolved = 0; + //printf("inbuf.(%s)\n",inbuf); + while (*inbuf) { + + if ((len = (int32_t)strlen(inbuf)) && inbuf[len-1] == '\n') { + len -= 1; + inbuf[len] = 0; + } + + count += 1; + if (len != PUZZLE_CELLS) { + fprintf(rejects, "%d: %s bogus puzzle format\n", count, inbuf); fflush(rejects); + *inbuf = 0; + bog += 1; + //if (h) fgets(inbuf, 128, h); + continue; + } + + cvt_to_grid(&g, inbuf); + if (g.givens < 17) { + fprintf(rejects, "%d: %*.*s bogus puzzle has less than 17 givens\n", count, PUZZLE_CELLS, PUZZLE_CELLS, inbuf); fflush(rejects); + *inbuf = 0; + bog += 1; + //if (h) fgets(inbuf, 128, h); + continue; + } + + for (s = soln_list; s;) { + s = soln_list->next; + free(soln_list); + soln_list = s; + } + + flag = rsolve(&g, add_soln); + if (soln_list) { + solved++; + for (solncount = 0, s = soln_list; s; s = s->next) { + solncount += 1; + if (prt_num) { + char nbuf[32]; + if (!enumerate_all) + sprintf(nbuf, "%d: ", count); + else + sprintf(nbuf, "%d:%d ", count, solncount); + fprintf(solnfile, "%-s", nbuf); + } + if (solncount > 1 || !enumerate_all) g.score = 0; + if (prt_score) fprintf(solnfile, "score: %-7d ", g.score); + if (prt_depth) fprintf(solnfile, "depth: %-3d ", g.maxlvl); + if (prt_answer || prt_grid) format_answer(s, outbuf); + if (prt_answer) fprintf(solnfile, "%s", outbuf); + if (prt_mask) fprintf(solnfile, " %s", cvt_to_mask(mbuf, inbuf)); + if (prt_givens) fprintf(solnfile, " %d", g.givens); + if (prt_grid) print_grid(outbuf, solnfile); + if (prt) fprintf(solnfile, "\n"); + if (s->next == NULL && prt_count) fprintf(solnfile, "count: %d\n", solncount); + } + if (solncount > 1 && enumerate_all) { + rc |= 1; + } + for (s = soln_list; s;) { + s = soln_list->next; + free(soln_list); + soln_list = s; + } + } + else { + unsolved++; + rc |= 1; + fprintf(rejects, "%d: %*.*s unsolved\n", count, PUZZLE_CELLS, PUZZLE_CELLS, inbuf); fflush(rejects); + diagnostic_grid(&g, rejects); +#if defined(DEBUG) + mypause(); +#endif + } + + *inbuf = 0; + //if (h) fgets(inbuf, 128, h); + } + + //if (prt) fprintf(solnfile, "\nPuzzles: %d, Solved: %d, Unsolved: %d, Bogus: %d\n", count, solved, unsolved, bog); + *scorep = g.score; + return solncount; +} +// end https://github.com/attractivechaos/plb/blob/master/sudoku/incoming/sudoku_solver.c + +// start https://github.com/mentalmove/SudokuGenerator +// +// main.c +// SudokuGenerator +// +// Malte Pagel +// + +#include +#include +#include +#include +#include + + +#define SMALL_LINE 3 +#define LINE 9 +#define TOTAL 81 + +#define LIMIT 16777216 + +#define SHOW_SOLVED 1 + + +struct dimensions_collection { + int row; + int column; + int small_square; +}; + + +static int indices[TOTAL]; +static int riddle[TOTAL]; +static int solved[TOTAL]; +static int unsolved[TOTAL]; +static int tries_to_set = 0; +static int taking_back; +static int global_unset_count = 0; + + +struct dimensions_collection get_collection(int); +int contains_element(int*, int, int); +void get_horizontal(int, int*); +void get_vertical(int, int*); +void get_square(int, int*); +int set_values(int, int); +void take_back(int); + +int show_solution(int*); + + +int show_solution (int* solution) { + + int i; + int counter = 0; + + printf( " -----------------------------------\n" ); + + for ( i = 0; i < TOTAL; i++ ) { + if ( i % LINE == 0 ) + printf( "|" ); + + if ( solution[i] ) { + printf( " %d ", solution[i]); + counter++; + } + else + printf( " "); + + if ( i % LINE == (LINE - 1) ) { + printf( "|\n" ); + if ( i != (TOTAL - 1) ) { + if ( i % (SMALL_LINE * LINE) == (SMALL_LINE * LINE - 1) ) + printf( "|-----------+-----------+-----------|\n" ); + else + printf( "|- - - - - -|- - - - - -|- - - - - -|\n" ); + } + } + else { + if ( i % SMALL_LINE == (SMALL_LINE - 1) ) + printf( "|"); + else + printf( ":" ); + } + } + + printf( " -----------------------------------" ); + + return counter; +} + + +/** + * Takes a position inside the large square and returns + * - the row number + * - the column number + * - the small square number + * where this position is situated in + */ +struct dimensions_collection get_collection (int index) { + struct dimensions_collection ret; + + ret.row = (int) (index / LINE); + ret.column = index % LINE; + ret.small_square = SMALL_LINE * (int) (ret.row / SMALL_LINE) + (int) (ret.column / SMALL_LINE); + + return ret; +} + +/** + * Is 'the_element' in 'the_array'? + */ +int contains_element (int* the_array, int the_element, int length) { + for ( int i = 0; i < length; i++ ) + if ( the_array[i] == the_element ) + return 1; + return 0; +} + +/** + * Sets all members of row 'row' + */ +void get_horizontal (int row, int* ret) { + int j = 0; + for ( int i = (row * LINE); i < (row * LINE) + LINE; i++ ) + ret[j++] = riddle[i]; +} +/** + * Sets all members of column 'col' + */ +void get_vertical (int col, int* ret) { + int j = 0; + for ( int i = col; i < TOTAL; i += LINE ) + ret[j++] = riddle[i]; +} +/** + * Sets all members of small square 'which' + */ +void get_square (int which, int* ret) { + for ( int i = 0; i < SMALL_LINE; i++ ) + for ( int j = 0; j < SMALL_LINE; j++ ) + ret[SMALL_LINE * i + j] = riddle[LINE * i + which * SMALL_LINE + j + ((int) (which / SMALL_LINE) * (SMALL_LINE - 1) * LINE)]; +} + +/** + * Recursive function: + * Try for each position the numbers from 1 to LINE + * (except 'forbidden_number' if given). + * If all numbers collide with already set numbers, move is bad. + * If a number doesn't collide with already set numbers, + * - move is bad if next move collides with the already set numbers + * (including actual one) + * - move is good if it's the last one + */ +int set_values (int index, int forbidden_number) { + + if ( taking_back && tries_to_set > (2 * LIMIT) ) + return 1; + + int real_index = indices[index]; + struct dimensions_collection blocks = get_collection(real_index); + int elements[LINE]; + + for ( int i = 1; i <= LINE; i++ ) { + if ( forbidden_number && i == forbidden_number ) + continue; + + tries_to_set++; + + get_horizontal(blocks.row, elements); + if ( contains_element(elements, i, LINE) ) + continue; + + get_vertical(blocks.column, elements); + if ( contains_element(elements, i, LINE) ) + continue; + + get_square(blocks.small_square, elements); + if ( contains_element(elements, i, LINE) ) + continue; + + riddle[real_index] = i; + + if ( index == (TOTAL - 1) || set_values((index + 1), 0) ) + return 1; + } + + riddle[real_index] = 0; + + return 0; +} + +/** + * Some steps to hide unnecessary numbers: + * a) Define last piece as 'special piece' + * b) Remember this piece's value + * c) Try to create riddle from this position on, + * but forbid the value of the special piece + * d) I) If operation fails, define the piece before the special piece + * as 'special piece' and continue with b) + * II) If operation is possible, reset 'special piece' + * and put it to start of list + * e) Stop if all pieces are tried or calculation limit is reached + */ +void take_back (int unset_count) { + + global_unset_count++; + + int i; + + int tmp = riddle[indices[TOTAL - unset_count]]; + int redundant = set_values((TOTAL - unset_count), tmp); + + if ( !redundant ) { + unsolved[indices[TOTAL - unset_count]] = 0; + take_back(++unset_count); + } + else { + riddle[indices[TOTAL - unset_count]] = tmp; + for ( i = 1; i < unset_count; i++ ) + riddle[indices[TOTAL - unset_count + i]] = 0; + + for ( i = (TOTAL - unset_count); i > 0; i-- ) + indices[i] = indices[i - 1]; + indices[0] = tmp; + + if ( global_unset_count < TOTAL && tries_to_set < LIMIT ) + take_back(unset_count); + } +} + + +int sudoku(uint8_t solved9[LINE][LINE],uint8_t unsolved9[LINE][LINE],uint32_t srandi) +{ + int i, j, random, small_rows, small_cols, tmp, redundant,ind; + int multi_raw[LINE][LINE]; + + memset(indices,0,sizeof(indices)); + memset(solved,0,sizeof(solved)); + memset(unsolved,0,sizeof(unsolved)); + tries_to_set = 0; + taking_back = 0; + global_unset_count = 0; + + //time_t t; + //time(&t); + srand(srandi); + + /** + * Initialization: + * Fields are set to 0 ( i.e. we dont' know the number yet) + */ + for ( i = 0; i < TOTAL; i++ ) + riddle[i] = 0; + + /** + * Second initialization: + * LINE times numbers from 0 to (LINE - 1), + * i.e. every square + */ + int big_rows_array[] = {0, 1, 2}; + int big_cols_array[] = {0, 1, 2}; + random = rand() % 4; + switch (random) { + case 1: + big_rows_array[0] = 2; + big_rows_array[1] = 1; + big_rows_array[2] = 0; + break; + case 2: + big_cols_array[0] = 2; + big_cols_array[1] = 1; + big_cols_array[2] = 0; + break; + case 3: + big_rows_array[0] = 2; + big_rows_array[1] = 1; + big_rows_array[2] = 0; + big_cols_array[0] = 2; + big_cols_array[1] = 1; + big_cols_array[2] = 0; + } + int big_rows, big_cols, big_rows_index, big_cols_index, start_value; + i = 0; + j = 0; + for ( big_rows_index = 0; big_rows_index < SMALL_LINE; big_rows_index++ ) { + big_rows = big_rows_array[big_rows_index]; + for ( big_cols_index = 0; big_cols_index < SMALL_LINE; big_cols_index++ ) { + big_cols = big_cols_array[big_cols_index]; + start_value = big_rows * LINE * SMALL_LINE + (big_cols * SMALL_LINE); + for ( small_rows = 0; small_rows < SMALL_LINE; small_rows++ ) + for ( small_cols = 0; small_cols < SMALL_LINE; small_cols++ ) + multi_raw[i][j++] = small_rows * LINE + small_cols + start_value; + i++; + j = 0; + } + } + + + /** + * Randomization for every element of multi_raw. + * Suffle only inside squares + */ + for ( i = 0; i < LINE; i++ ) { + for ( j = 0; j < LINE; j++ ) { + random = rand() % LINE; + if ( j == random ) + continue; + tmp = multi_raw[i][j]; + multi_raw[i][j] = multi_raw[i][random]; + multi_raw[i][random] = tmp; + } + } + + /** + * Linearization + */ + for ( i = 0; i < LINE; i++ ) + for ( j = 0; j < LINE; j++ ) + indices[i * LINE + j] = multi_raw[i][j]; + + + /** + * Setting numbers, start with the first one. + * Variable 'redundant' is needed only for formal reasons + */ + taking_back = 0; + redundant = set_values(0, 0); + + + memcpy(solved, riddle, (TOTAL * sizeof(int))); + memcpy(unsolved, riddle, (TOTAL * sizeof(int))); + + + /** + * Exchanging some (few) indices for more randomized game + */ + int random2; + for ( i = (LINE - 1); i > 0; i-- ) { + for ( j = 0; j < (int) (sqrt(i)); j++ ) { + + if ( !(rand() % ((int) (i * sqrt(i)))) || !(LINE - j) ) + continue; + + random = i * LINE + (int) (rand() % (LINE - j)); + random2 = rand() % TOTAL; + + if ( random == random2 ) + continue; + + tmp = indices[random]; + indices[random] = indices[random2]; + indices[random2] = tmp; + } + } + + + tries_to_set = 0; + taking_back = 1; + take_back(1); + + + if ( SHOW_SOLVED ) { + printf( "\n\n" ); + redundant = show_solution(solved); + } + + int counter = show_solution(unsolved); + printf( "\t *** %d numbers left *** \n", counter ); + ind = 0; + for (i=0; i=0; j--) + { + x *= 9; + x += vals9[i][j]-1; + } + if ( i < 8 ) + keyvals[i] = x; + else + { + for (j=0; j<8; j++) + keyvals[j] += SUDOKU_NINETH * (vals9[i][j]-1); + } + } + for (i=ind=0; i<8; i++) + { + privkey[ind++] = ((keyvals[i] >> 24) & 0xff); + privkey[ind++] = ((keyvals[i] >> 16) & 0xff); + privkey[ind++] = ((keyvals[i] >> 8) & 0xff); + privkey[ind++] = (keyvals[i] & 0xff); + } +} + +void sudoku_gen(uint8_t key32[32],uint8_t unsolved[9][9],uint32_t srandi) +{ + uint8_t vals9[9][9],uniq9[9][9]; int32_t i,j; + sudoku(vals9,unsolved,srandi); + sudoku_privkey(key32,vals9); + sudoku_privkeydisp(key32); +} + +//////////////////////// start of CClib interface +// ./komodod -ac_name=SUDOKU -ac_supply=1000000 -pubkey= -addnode=5.9.102.210 -gen -genproclimit=1 -ac_cclib=sudoku -ac_perc=10000000 -ac_reward=100000000 -ac_cc=60000 -ac_script=2ea22c80203d1579313abe7d8ea85f48c65ea66fc512c878c0d0e6f6d54036669de940febf8103120c008203000401cc & +/* cclib "gen" 17 \"10\" + 5d13c1ad80daf37215c74809a36720c2ada90bacadb2e10bf0866092ce558432 +*/ + +/* cclib "txidinfo" 17 \"5d13c1ad80daf37215c74809a36720c2ada90bacadb2e10bf0866092ce558432\" +{ + "result": "success", + "txid": "5d13c1ad80daf37215c74809a36720c2ada90bacadb2e10bf0866092ce558432", + "result": "success", + "amount": 1.00000000, + "unsolved": "46-8---15-75-61-3----4----8-1--75-----3--24----2-----6-4----------73----------36-", + "name": "sudoku", + "method": "txidinfo" +}*/ + +/* cclib "pending" 17 +{ + "result": "success", + "name": "sudoku", + "method": "pending", + "pending": [ + "5d13c1ad80daf37215c74809a36720c2ada90bacadb2e10bf0866092ce558432" + ] +}*/ + +/* + cclib "solution" 17 \"[%22fdc9409741f2ede29307da1a06438da0ea6f8d885d2d5c3199c4ef541ec1b5fd%22,%22469823715875961234231457698914675823653182479782394156346219587528736941197548362%22,1548777525,1548777526,...]\" + { + "name": "sudoku", + "method": "solution", + "sudokuaddr": "RSeoPJvMUSLfUHM1BomB97geW9zPznwHXk", + "amount": 1.00000000, + "result": "success", + "hex": "0400008085202f8901328455ce926086f00be1b2adac0ba9adc22067a30948c71572f3da80adc1135d010000007b4c79a276a072a26ba067a565802102c57d40c1ddc92a5246a937bd7338823f1e8c916b137f2092d38cf250d74cb5ab8140f92d54f611aa3cb3d187eaadd56b06f3a8c0f5fba23956b26fdefc6038d9b6282de38525f72ebd8945a7994cef63ebca711ecf8fe6baeefcc218cf58efb59dc2a100af03800111a10001ffffffff02f0b9f505000000002321039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775ac0000000000000000fd9f016a4d9b01115351343639383233373135383735393631323334323331343537363938393134363735383233363533313832343739373832333934313536333436323139353837353238373336393431313937353438333632fd4401000000005c5078355c50783600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000" + } + + cclib solution 17 \"[%224d50336780d5a300a1f01b12fe36f46a82f3b9935bb115e01e0113dc4f337aae%22,%22234791685716258943589643712865934127341827596927516438492375861178462359653189274%22,0,0,1548859143,1548859146,0,1548859146,0,1548859148,1548859149,0,1548859151,1548859152,0,1548859154,1548859155,1548859158,1548859159,0,0,0,1548859161,1548859163,0,1548859164,1548859168,0,1548859168,1548859170,1548859172,1548859172,1548859175,0,0,1548859176,0,0,1548859178,1548859178,0,0,1548859180,1548859181,1548859183,1548859184,1548859185,1548859186,1548859188,1548859190,1548859191,1548859192,1548859192,0,0,1548859195,1548859196,1548859197,1548859198,0,0,1548859199,1548859202,1548859202,0,1548859204,1548859205,1548859206,1548859209,1548859210,1548859211,1548859212,0,1548859214,1548859216,0,1548859217,1548859218,1548859219,1548859220,0,1548859222,1548859222]\" + */ + +int32_t sudoku_captcha(int32_t dispflag,uint32_t timestamps[81],int32_t height) +{ + int32_t i,solvetime,diff,avetime,n = 0,retval = 0; uint64_t variance = 0; std::vector list; + for (i=0; i<81; i++) + { + if ( timestamps[i] != 0 ) + { + list.push_back(timestamps[i]); + n++; + } + } + if ( n > 81/2 ) + { + std::sort(list.begin(),list.end()); + solvetime = (list[n-1] - list[0]); + if ( list[0] >= list[n-1] ) + { + printf("list[0] %u vs list[%d-1] %u\n",list[0],n,list[n-1]); + retval = -1; + } + else if ( list[n-1] > chainActive.LastTip()->nTime+200 ) + retval = -2; + else if ( solvetime >= 777 ) + retval = 0; + else + { + avetime = (solvetime / (n-1)); + if ( avetime == 0 ) + retval = -3; + for (i=0; i>>>>>>>>>>>>>> ht.%d retval.%d\n",height,retval); + if ( height <= 2036 ) + return(0); + else return(retval); +} + +CScript sudoku_genopret(uint8_t unsolved[9][9]) +{ + CScript opret; uint8_t evalcode = EVAL_SUDOKU; std::vector data; int32_t i,j; + for (i=0; i<9; i++) + for (j=0; j<9; j++) + data.push_back(unsolved[i][j]); + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'G' << data); + return(opret); +} + +CScript sudoku_solutionopret(char *solution,uint32_t timestamps[81]) +{ + CScript opret; uint8_t evalcode = EVAL_SUDOKU; std::string str(solution); std::vector data; int32_t i; + for (i=0; i<81; i++) + { + data.push_back((timestamps[i] >> 24) & 0xff); + data.push_back((timestamps[i] >> 16) & 0xff); + data.push_back((timestamps[i] >> 8) & 0xff); + data.push_back(timestamps[i] & 0xff); + } + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << str << data); + return(opret); +} + +uint8_t sudoku_solutionopreturndecode(char solution[82],uint32_t timestamps[81],CScript scriptPubKey) +{ + std::vector vopret; uint8_t *script,e,f; std::string str; std::vector data; int32_t i,ind; uint32_t x; + GetOpReturnData(scriptPubKey,vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> str; ss >> data) != 0 && e == EVAL_SUDOKU && f == 'S' ) + { + if ( data.size() == 81*sizeof(uint32_t) && str.size() == 81 ) + { + strcpy(solution,str.c_str()); + for (i=ind=0; i<81; i++) + { + if ( solution[i] < '1' || solution[i] > '9' ) + break; + x = data[ind++]; + x <<= 8, x |= (data[ind++] & 0xff); + x <<= 8, x |= (data[ind++] & 0xff); + x <<= 8, x |= (data[ind++] & 0xff); + timestamps[i] = x; + } + if ( i == 81 ) + return(f); + } else fprintf(stderr,"datasize %d sol[%d]\n",(int32_t)data.size(),(int32_t)str.size()); + } + return(0); +} + +uint8_t sudoku_genopreturndecode(char *unsolved,CScript scriptPubKey) +{ + std::vector vopret; uint8_t *script,e,f; std::vector data; int32_t i; + GetOpReturnData(scriptPubKey,vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> data) != 0 && e == EVAL_SUDOKU && f == 'G' ) + { + if ( data.size() == 81 ) + { + for (i=0; i<81; i++) + unsolved[i] = data[i] == 0 ? '-' : '0' + data[i]; + unsolved[i] = 0; + return(f); + } + } + return(0); +} + +UniValue sudoku_generate(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); CPubKey sudokupk,pk; uint8_t privkey[32],unsolved[9][9],pub33[33]; uint32_t srandi; int32_t i,score; uint256 hash; char coinaddr[64],str[82],*jsonstr; uint64_t inputsum,amount,change=0; std::string rawtx; + amount = COIN; + /*if ( params != 0 ) + { + if ( (jsonstr= jprint(params,0)) != 0 ) + { + if ( jsonstr[0] == '"' && jsonstr[strlen(jsonstr)-1] == '"' ) + { + jsonstr[strlen(jsonstr)-1] = 0; + jsonstr++; + } + amount = atof(jsonstr) * COIN + 0.0000000049; + } + }*/ + result.push_back(Pair("result","success")); + result.push_back(Pair("name","sudoku")); + result.push_back(Pair("method","gen")); + hash = chainActive.LastTip()->GetBlockHash(); + memcpy(&srandi,&hash,sizeof(srandi)); + srandi ^= (uint32_t)time(NULL); + while ( 1 ) + { + sudoku_gen(privkey,unsolved,srandi); + for (i=0; iunspendableCCaddr,1)) >= amount+2*txfee ) + { + //printf("inputsum %.8f\n",(double)inputsum/COIN); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,sudokupk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pk)); + if ( inputsum > amount + 2*txfee ) + change = (inputsum - amount - 2*txfee); + if ( change > txfee ) + { + if ( change > 10000*COIN ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,change/2,sudokupk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,change/2,sudokupk)); + } else mtx.vout.push_back(MakeCC1vout(cp->evalcode,change,sudokupk)); + } + rawtx = FinalizeCCTx(0,cp,mtx,pubkey2pk(Mypubkey()),txfee,sudoku_genopret(unsolved)); + if ( rawtx.size() > 0 ) + { + CTransaction tx; + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + LOCK(cs_main); + if ( myAddtomempool(tx) != 0 ) + { + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + } + } + } else result.push_back(Pair("error","couldnt finalize CCtx")); + } else result.push_back(Pair("error","not enough SUDOKU funds")); + return(result); +} + +UniValue sudoku_txidinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); int32_t numvouts; char CCaddr[64],str[65],*txidstr; uint256 txid,hashBlock; CTransaction tx; char unsolved[82]; CBlockIndex *pindex; + if ( params != 0 ) + { + result.push_back(Pair("result","success")); + if ( (txidstr= jprint(params,0)) != 0 ) + { + if ( txidstr[0] == '"' && txidstr[strlen(txidstr)-1] == '"' ) + { + txidstr[strlen(txidstr)-1] = 0; + txidstr++; + } + //printf("params -> (%s)\n",txidstr); + decode_hex((uint8_t *)&txid,32,txidstr); + txid = revuint256(txid); + result.push_back(Pair("txid",txid.GetHex())); + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( sudoku_genopreturndecode(unsolved,tx.vout[numvouts-1].scriptPubKey) == 'G' ) + { + result.push_back(Pair("result","success")); + if ( (pindex= komodo_blockindex(hashBlock)) != 0 ) + result.push_back(Pair("height",pindex->GetHeight())); + Getscriptaddress(CCaddr,tx.vout[1].scriptPubKey); + result.push_back(Pair("sudokuaddr",CCaddr)); + result.push_back(Pair("amount",ValueFromAmount(tx.vout[1].nValue))); + result.push_back(Pair("unsolved",unsolved)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt extract sudoku_generate opreturn")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find txid")); + } + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","missing txid in params")); + } + result.push_back(Pair("name","sudoku")); + result.push_back(Pair("method","txidinfo")); + return(result); +} + +UniValue sudoku_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); + char coinaddr[64],unsolved[82]; int64_t nValue,total=0; uint256 txid,hashBlock; CTransaction tx; int32_t vout,numvouts; CPubKey sudokupk; CBlockIndex *pindex; + std::vector > unspentOutputs; + sudokupk = GetUnspendable(cp,0); + GetCCaddress(cp,coinaddr,sudokupk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + //char str[65]; fprintf(stderr,"%s check %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); + if ( it->second.satoshis != txfee || vout != 0 ) + continue; + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + if ( (nValue= IsCClibvout(cp,tx,vout,coinaddr)) == txfee && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + if ( sudoku_genopreturndecode(unsolved,tx.vout[numvouts-1].scriptPubKey) == 'G' ) + { + UniValue obj(UniValue::VOBJ); + if ( (pindex= komodo_blockindex(hashBlock)) != 0 ) + obj.push_back(Pair("height",pindex->GetHeight())); + obj.push_back(Pair("amount",ValueFromAmount(tx.vout[1].nValue))); + obj.push_back(Pair("txid",txid.GetHex())); + a.push_back(obj); + total += tx.vout[1].nValue; + } + } + } + } + result.push_back(Pair("result","success")); + result.push_back(Pair("name","sudoku")); + result.push_back(Pair("method","pending")); + result.push_back(Pair("pending",a)); + result.push_back(Pair("numpending",(int64_t)a.size())); + result.push_back(Pair("total",ValueFromAmount(total))); + return(result); +} + +UniValue sudoku_solution(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); int32_t i,j,good,ind,n,numvouts; uint256 txid; char *jsonstr,*newstr,*txidstr,coinaddr[64],checkaddr[64],CCaddr[64],*solution=0,unsolved[82]; CPubKey pk,mypk; uint8_t vals9[9][9],priv32[32],pub33[33]; uint32_t timestamps[81]; uint64_t balance,inputsum; std::string rawtx; CTransaction tx; uint256 hashBlock; + mypk = pubkey2pk(Mypubkey()); + memset(timestamps,0,sizeof(timestamps)); + result.push_back(Pair("name","sudoku")); + result.push_back(Pair("method","solution")); + good = 0; + if ( params != 0 ) + { + if ( params != 0 && (n= cJSON_GetArraySize(params)) > 0 ) + { + if ( n > 2 && n <= (sizeof(timestamps)/sizeof(*timestamps))+2 ) + { + for (i=2; i '9' ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","illegal solution")); + return(result); + } + vals9[i][j] = solution[ind++] - '0'; + } + sudoku_privkey(priv32,vals9); + priv2addr(coinaddr,pub33,priv32); + pk = buf2pk(pub33); + GetCCaddress(cp,CCaddr,pk); + result.push_back(Pair("sudokuaddr",CCaddr)); + balance = CCaddress_balance(CCaddr,1); + result.push_back(Pair("amount",ValueFromAmount(balance))); + if ( sudoku_captcha(1,timestamps,komodo_nextheight()) < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","captcha failure")); + return(result); + } + else + { + if ( (txidstr= jstri(params,0)) != 0 ) + { + decode_hex((uint8_t *)&txid,32,txidstr); + txid = revuint256(txid); + result.push_back(Pair("txid",txid.GetHex())); + if ( CCgettxout(txid,0,1,0) < 0 ) + result.push_back(Pair("error","already solved")); + else if ( GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 1 ) + { + Getscriptaddress(checkaddr,tx.vout[1].scriptPubKey); + if ( strcmp(checkaddr,CCaddr) != 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","wrong solution")); + result.push_back(Pair("yours",CCaddr)); + return(result); + } + if ( sudoku_genopreturndecode(unsolved,tx.vout[numvouts-1].scriptPubKey) == 'G' ) + { + for (i=0; i<81; i++) + { + if ( unsolved[i] < '1' || unsolved[i] > '9') + continue; + else if ( unsolved[i] != solution[i] ) + { + printf("i.%d [%c] != [%c]\n",i,unsolved[i],solution[i]); + result.push_back(Pair("error","wrong sudoku solved")); + break; + } + } + if ( i == 81 ) + good = 1; + } else result.push_back(Pair("error","cant decode sudoku")); + } else result.push_back(Pair("error","couldnt find sudoku")); + } + if ( good != 0 ) + { + mtx.vin.push_back(CTxIn(txid,0,CScript())); + if ( (inputsum= AddCClibInputs(cp,mtx,pk,balance,16,CCaddr,1)) >= balance ) + { + mtx.vout.push_back(CTxOut(balance,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + CCaddr2set(cp,cp->evalcode,pk,priv32,CCaddr); + rawtx = FinalizeCCTx(0,cp,mtx,pubkey2pk(Mypubkey()),txfee,sudoku_solutionopret(solution,timestamps)); + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("hex",rawtx)); + } + else result.push_back(Pair("error","couldnt finalize CCtx")); + } else result.push_back(Pair("error","couldnt find funds in solution address")); + } + } + } + } + else + { + printf("n.%d params.(%s)\n",n,jprint(params,0)); + result.push_back(Pair("error","couldnt get all params")); + } + return(result); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt parse parameters")); + result.push_back(Pair("parameters",newstr)); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","missing parameters")); + return(result); +} + +int32_t sudoku_minval(uint32_t timestamps[81]) +{ + int32_t i,ind = -1; uint32_t mintimestamp = 0xffffffff; + for (i=0; i<81; i++) + if ( timestamps[i] != 0 && timestamps[i] < mintimestamp ) + { + mintimestamp = timestamps[i], ind = i; + //fprintf(stderr,"%d ",i); + } + //fprintf(stderr,"mintimestamp.%u\n",mintimestamp); + return(ind); +} + +bool sudoku_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + static char laststr[512]; + CScript scriptPubKey; std::vector vopret; uint8_t *script,e,f,funcid; int32_t i,ind,errflag,dispflag,score,numvouts; char unsolved[82],solution[82],str[512]; uint32_t timestamps[81]; CTransaction vintx; uint256 hashBlock; + if ( (numvouts= tx.vout.size()) > 1 ) + { + scriptPubKey = tx.vout[numvouts-1].scriptPubKey; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 ) + { + script = (uint8_t *)vopret.data(); + if ( script[0] == EVAL_SUDOKU ) + { + switch ( script[1] ) + { + case 'G': + if ( sudoku_genopreturndecode(unsolved,scriptPubKey) == 'G' ) + { + //fprintf(stderr,"unsolved.(%s)\n",unsolved); + if ( dupree_solver(0,&score,unsolved) != 1 || score*COIN != tx.vout[1].nValue ) + { + sprintf(str,"ht.%d score.%d vs %.8f %s",height,score,(double)tx.vout[1].nValue/COIN,tx.GetHash().ToString().c_str()); + if ( strcmp(str,laststr) != 0 ) + { + strcpy(laststr,str); + fprintf(stderr,"%s\n",str); + } + if ( strcmp(ASSETCHAINS_SYMBOL,"SUDOKU") != 0 || height > 2000 ) + return eval->Invalid("mismatched sudoku value vs score"); + else return(true); + } else return(true); + } + fprintf(stderr,"height.%d txid.%s\n",height,tx.GetHash().ToString().c_str()); + return eval->Invalid("invalid generate opreturn"); + case 'S': + sprintf(str,"SOLVED ht.%d %.8f %s",height,(double)tx.vout[0].nValue/COIN,tx.GetHash().ToString().c_str()); + if ( strcmp(str,laststr) != 0 ) + { + strcpy(laststr,str); + fprintf(stderr,"%s\n",str); + dispflag = 1; + } else dispflag = 0; + if ( sudoku_solutionopreturndecode(solution,timestamps,scriptPubKey) == 'S' ) + { + if ( tx.vin.size() > 1 && tx.vin[0].prevout.hash == tx.vin[1].prevout.hash && tx.vin[0].prevout.n == 0 && tx.vin[1].prevout.n == 1 && myGetTransaction(tx.vin[0].prevout.hash,vintx,hashBlock) != 0 ) + { + if ( vintx.vout.size() > 1 && sudoku_genopreturndecode(unsolved,vintx.vout[vintx.vout.size()-1].scriptPubKey) == 'G' ) + { + for (i=errflag=0; i<81; i++) + { + if ( 0 && dispflag != 0 ) + fprintf(stderr,"%u ",timestamps[i]); + if ( (timestamps[i] != 0 && unsolved[i] >= '1' && unsolved[i] <= '9') || (timestamps[i] == 0 && (unsolved[i] < '1' || unsolved[i] > '9')) ) + errflag++; + } + if ( errflag != 0 ) + { + if ( dispflag != 0 ) + fprintf(stderr,"ht.%d errflag.%d %s\n",height,errflag,unsolved); + if ( (height != 1220 && height != 1383) || strcmp(ASSETCHAINS_SYMBOL,"SUDOKU") != 0 ) + return eval->Invalid("invalid timestamp vs unsolved"); + } + if ( dupree_solver(0,&score,unsolved) != 1 ) + { + if ( dispflag != 0 ) + fprintf(stderr,"non-unique sudoku at ht.%d\n",height); + if ( strcmp(ASSETCHAINS_SYMBOL,"SUDOKU") != 0 ) + return eval->Invalid("invalid sudoku with multiple solutions"); + } + if ( dispflag != 0 ) + fprintf(stderr,"%s score.%d %s\n",solution,score,unsolved); + if ( sudoku_captcha(dispflag,timestamps,height) < 0 ) + return eval->Invalid("failed captcha"); + /*for (i=lasttime=0; i<81; i++) + { + if ( (ind= sudoku_minval(timestamps)) >= 0 ) + { + unsolved[ind] = solution[ind]; + if ( lasttime == 0 ) + lasttime = timestamps[ind]; + if ( dupree_solver(0,&score,unsolved) != 1 ) + fprintf(stderr,"i.%d ind.%d non-unique\n",i,ind); + if ( dispflag != 0 ) + fprintf(stderr,"%d.%d ",score,timestamps[ind]-lasttime); + lasttime = timestamps[ind]; + timestamps[ind] = 0; + } else break; + } + if ( dispflag != 0 ) + fprintf(stderr,"scores convergence\n");*/ + return(true); + } else return eval->Invalid("invalid solution opret"); + } + else if ( strcmp(ASSETCHAINS_SYMBOL,"SUDOKU") == 0 && height == 236 ) + return(true); + else return eval->Invalid("invalid solution vin"); + } + fprintf(stderr,"solution ht.%d %s bad opret\n",height,tx.GetHash().ToString().c_str()); + return eval->Invalid("invalid solution opreturn"); + default: return eval->Invalid("invalid funcid"); + } + } else return eval->Invalid("invalid evalcode"); + + } + } + return eval->Invalid("not enough vouts"); +} + + diff --git a/src/chain.h b/src/chain.h index ee56c7662..d810ed4cb 100644 --- a/src/chain.h +++ b/src/chain.h @@ -102,6 +102,9 @@ enum BlockStatus: uint32_t { //! Scripts & signatures ok. Implies all parents are also at least SCRIPTS. BLOCK_VALID_SCRIPTS = 5, + // flag to check if contextual check block has passed in Accept block, if it has not check at connect block. + BLOCK_VALID_CONTEXT = 6, + //! All validity bits. BLOCK_VALID_MASK = BLOCK_VALID_HEADER | BLOCK_VALID_TREE | BLOCK_VALID_TRANSACTIONS | BLOCK_VALID_CHAIN | BLOCK_VALID_SCRIPTS, @@ -115,7 +118,7 @@ enum BlockStatus: uint32_t { BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade - BLOCK_IN_TMPFILE = 256 + BLOCK_IN_TMPFILE = 256 }; //! Short-hand for the highest consensus validity we implement. diff --git a/src/chainparams.cpp b/src/chainparams.cpp index 5c9efb276..26e29d9de 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -91,7 +91,7 @@ static CBlock CreateGenesisBlock(uint32_t nTime, const uint256& nNonce, const st */ void *chainparams_commandline(void *ptr); #include "komodo_defs.h" - +int32_t ASSETCHAINS_BLOCKTIME = 60; const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")); @@ -276,6 +276,11 @@ void *chainparams_commandline(void *ptr) //fprintf(stderr,">>>>>>>> port.%u\n",ASSETCHAINS_P2PPORT); if ( ASSETCHAINS_SYMBOL[0] != 0 ) { + if ( ASSETCHAINS_BLOCKTIME != 60 ) + { + mainParams.consensus.nMaxFutureBlockTime = 7 * ASSETCHAINS_BLOCKTIME; // 7 blocks + mainParams.consensus.nPowTargetSpacing = ASSETCHAINS_BLOCKTIME; + } mainParams.SetDefaultPort(ASSETCHAINS_P2PPORT); if ( ASSETCHAINS_RPCPORT == 0 ) ASSETCHAINS_RPCPORT = ASSETCHAINS_P2PPORT + 1; @@ -284,15 +289,22 @@ void *chainparams_commandline(void *ptr) mainParams.pchMessageStart[2] = (ASSETCHAINS_MAGIC >> 16) & 0xff; mainParams.pchMessageStart[3] = (ASSETCHAINS_MAGIC >> 24) & 0xff; fprintf(stderr,">>>>>>>>>> %s: p2p.%u rpc.%u magic.%08x %u %u coins\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_MAGIC,ASSETCHAINS_MAGIC,(uint32_t)ASSETCHAINS_SUPPLY); - - if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH) + if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH) { // this is only good for 60 second blocks with an averaging window of 45. for other parameters, use: - // nLwmaAjustedWeight = (N+1)/2 * (0.9989^(500/nPowAveragingWindow)) * nPowTargetSpacing + // nLwmaAjustedWeight = (N+1)/2 * (0.9989^(500/nPowAveragingWindow)) * nPowTargetSpacing mainParams.consensus.nLwmaAjustedWeight = 1350; mainParams.consensus.nPowAveragingWindow = 45; mainParams.consensus.powAlternate = uint256S("00000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); } + else if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1) + { + // this is only good for 60 second blocks with an averaging window of 45. for other parameters, use: + // nLwmaAjustedWeight = (N+1)/2 * (0.9989^(500/nPowAveragingWindow)) * nPowTargetSpacing + mainParams.consensus.nLwmaAjustedWeight = 1350; + mainParams.consensus.nPowAveragingWindow = 45; + mainParams.consensus.powAlternate = uint256S("0000000f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f0f"); + } if (ASSETCHAINS_LWMAPOS != 0) { @@ -663,7 +675,7 @@ public: BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); nEquihashN = N; nEquihashK = K; - + genesis = CreateGenesisBlock( 1296688602, uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"), diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 4f004aac2..084bbd5ce 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -20,6 +20,7 @@ #include "clientversion.h" #include "tinyformat.h" +#include "util.h" #include @@ -34,7 +35,7 @@ * for both bitcoind and bitcoin-core, to make it harder for attackers to * target servers or GUI users specifically. */ -const std::string CLIENT_NAME("MagicBean"); +const std::string CLIENT_NAME = GetArg("-clientname", "MagicBean"); /** * Client version number diff --git a/src/coins.h b/src/coins.h index f83fda7d2..e0ea7d822 100644 --- a/src/coins.h +++ b/src/coins.h @@ -446,7 +446,7 @@ class CCoinsViewCache; /** * A reference to a mutable cache entry. Encapsulating it allows us to run * cleanup code after the modification is finished, and keeping track of - * concurrent modifications. + * concurrent modifications. */ class CCoinsModifier { @@ -503,7 +503,7 @@ protected: /** * Make mutable so that we can "fill the cache" even from Get-methods - * declared as "const". + * declared as "const". */ mutable uint256 hashBlock; mutable CCoinsMap cacheCoins; diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 8b6cb12a4..d65c5dd8c 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -66,7 +66,7 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh int seenOwnNotarisations = 0; - bool txscl = IsTXSCL(symbol); + int authority = GetSymbolAuthority(symbol); for (int i=0; i kmdHeight) break; @@ -90,13 +90,20 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh if (seenOwnNotarisations == 1) { BOOST_FOREACH(Notarisation& nota, notarisations) { - if (IsTXSCL(nota.second.symbol) == txscl) - if (nota.second.ccId == targetCCid) - moms.push_back(nota.second.MoM); + if (GetSymbolAuthority(nota.second.symbol) == authority) + if (nota.second.ccId == targetCCid) { + moms.push_back(nota.second.MoM); + //fprintf(stderr, "added mom: %s\n",nota.second.MoM.GetHex().data()); + } } } } + // Not enough own notarisations found to return determinate MoMoM + destNotarisationTxid = uint256(); + moms.clear(); + return uint256(); + end: return GetMerkleRoot(moms); } diff --git a/src/crosschain.h b/src/crosschain.h index b97afde98..b3d1af9b2 100644 --- a/src/crosschain.h +++ b/src/crosschain.h @@ -18,6 +18,18 @@ #include "cc/eval.h" +const int CROSSCHAIN_KOMODO = 1; +const int CROSSCHAIN_TXSCL = 2; +const int CROSSCHAIN_STAKED = 3; + +typedef struct CrosschainAuthority { + uint8_t notaries[64][33]; + int8_t size; + int8_t requiredSigs; +} CrosschainAuthority; + +int GetSymbolAuthority(const char* symbol); +bool CheckTxAuthority(const CTransaction &tx, CrosschainAuthority auth); /* On assetchain */ TxProof GetAssetchainProof(uint256 hash,CTransaction burnTx); diff --git a/src/crosschain_authority.cpp b/src/crosschain_authority.cpp new file mode 100644 index 000000000..7487e4879 --- /dev/null +++ b/src/crosschain_authority.cpp @@ -0,0 +1,70 @@ +#include "cc/eval.h" +#include "crosschain.h" +#include "notarisationdb.h" +#include "notaries_staked.h" + +int GetSymbolAuthority(const char* symbol) +{ + if (strncmp(symbol, "TXSCL", 5) == 0) + return CROSSCHAIN_TXSCL; + if (is_STAKED(symbol) != 0) { + //printf("RETURNED CROSSCHAIN STAKED AS TRUE\n"); + return CROSSCHAIN_STAKED; + } + //printf("RETURNED CROSSCHAIN KOMODO AS TRUE\n"); + return CROSSCHAIN_KOMODO; +} + + +bool CheckTxAuthority(const CTransaction &tx, CrosschainAuthority auth) +{ + EvalRef eval; + + if (tx.vin.size() < auth.requiredSigs) return false; + + uint8_t seen[64] = {0}; + + BOOST_FOREACH(const CTxIn &txIn, tx.vin) + { + // Get notary pubkey + CTransaction tx; + uint256 hashBlock; + if (!eval->GetTxUnconfirmed(txIn.prevout.hash, tx, hashBlock)) return false; + if (tx.vout.size() < txIn.prevout.n) return false; + CScript spk = tx.vout[txIn.prevout.n].scriptPubKey; + if (spk.size() != 35) return false; + const unsigned char *pk = &spk[0]; + if (pk++[0] != 33) return false; + if (pk[33] != OP_CHECKSIG) return false; + + // Check it's a notary + for (int i=0; ilength > b->length) ret = 1; + // else if ( (uint64_t)a < (uint64_t)b ) // jl777 prevent nondeterminism + // ret = -1; + // else ret = 1; } return ret; diff --git a/src/cryptoconditions/src/include/cJSON.h b/src/cryptoconditions/src/include/cJSON.h index 2a6139680..547dddad5 100644 --- a/src/cryptoconditions/src/include/cJSON.h +++ b/src/cryptoconditions/src/include/cJSON.h @@ -35,8 +35,8 @@ * * ******************************************************************************/ -#ifndef cJSON__h -#define cJSON__h +#ifndef cJSON__cch +#define cJSON__cch #ifdef __cplusplus extern "C" diff --git a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h index 72bf7d758..bdb9ae43a 100644 --- a/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h +++ b/src/cryptoconditions/src/include/secp256k1/src/ecmult_const.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2015 Andrew Poelstra * * Distributed under the MIT software license, see the accompanying * @@ -13,3 +15,25 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); #endif /* SECP256K1_ECMULT_CONST_H */ + +#else + +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_CONST_H +#define SECP256K1_ECMULT_CONST_H + +#include "scalar.h" +#include "group.h" + +/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus + * one because we internally sometimes add 2 to the number during the WNAF conversion. */ +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); + + +#endif + diff --git a/src/cryptoconditions/src/secp256k1.c b/src/cryptoconditions/src/secp256k1.c index 31bab06e5..7cb557fb0 100644 --- a/src/cryptoconditions/src/secp256k1.c +++ b/src/cryptoconditions/src/secp256k1.c @@ -68,7 +68,7 @@ void lockSign() { } if (!secp256k1_context_randomize(ec_ctx_sign, ent)) { fprintf(stderr, "Could not randomize secp256k1 context\n"); - exit(1); + exit(-1); } } diff --git a/src/cryptoconditions/src/threshold.c b/src/cryptoconditions/src/threshold.c index 82f0e1b0a..9547f4f8c 100644 --- a/src/cryptoconditions/src/threshold.c +++ b/src/cryptoconditions/src/threshold.c @@ -35,8 +35,16 @@ static uint32_t thresholdSubtypes(const CC *cond) { } -static int cmpCostDesc(const void *a, const void *b) { - return (int) ( *(unsigned long*)b - *(unsigned long*)a ); +static int cmpCostDesc(const void *a, const void *b) +{ + int retval; + retval = (int) ( *(unsigned long*)b - *(unsigned long*)a ); + return(retval); + /*if ( retval != 0 ) + return(retval); + else if ( (uint64_t)a < (uint64_t)b ) // jl777 prevent nondeterminism + return(-1); + else return(1);*/ } @@ -79,7 +87,10 @@ static int cmpConditionBin(const void *a, const void *b) { if (ret == 0) return r0.encoded < r1.encoded ? -1 : 1; - return 0; + //else if ( (uint64_t)a < (uint64_t)b ) // jl777 prevent nondeterminism + // return(-1); + //else return(1); + return(0); } diff --git a/src/fiat/ilien b/src/fiat/ilien new file mode 100755 index 000000000..53e571661 --- /dev/null +++ b/src/fiat/ilien @@ -0,0 +1,3 @@ +#!/bin/bash +./komodo-cli -ac_name=ILN $1 $2 $3 $4 $5 $6 + diff --git a/src/fiat/iln b/src/fiat/iln new file mode 100755 index 000000000..f453c338e --- /dev/null +++ b/src/fiat/iln @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=ILN $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/morty b/src/fiat/morty new file mode 100755 index 000000000..4579324b7 --- /dev/null +++ b/src/fiat/morty @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=MORTY $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/rick b/src/fiat/rick new file mode 100755 index 000000000..b68bd56ab --- /dev/null +++ b/src/fiat/rick @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=RICK $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/vote2019 b/src/fiat/vote2019 new file mode 100755 index 000000000..029558808 --- /dev/null +++ b/src/fiat/vote2019 @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=VOTE2019 $1 $2 $3 $4 $5 $6 diff --git a/src/gui/komodoku/README.md b/src/gui/komodoku/README.md new file mode 100644 index 000000000..8a3778ea0 --- /dev/null +++ b/src/gui/komodoku/README.md @@ -0,0 +1,27 @@ +About +----- +Komodo SudokuCC GUI + +Just solve Sudoku and earn SUDOKU coins! + +![alt text](https://i.imgur.com/std99XW.png) + +To run you need up and running SUDOKU chain daemon built from latest https://github.com/jl777/komodo/tree/FSM and started with valid for your wallet pubkey in `-pubkey=` param. + +SUDOKU chain params: +```./komodod -ac_name=SUDOKU -ac_supply=1000000 -pubkey= -addnode=5.9.102.210 -gen -genproclimit=1 -ac_cclib=sudoku -ac_perc=10000000 -ac_reward=100000000 -ac_cc=60000 -ac_script=2ea22c80203d1579313abe7d8ea85f48c65ea66fc512c878c0d0e6f6d54036669de940febf8103120c008203000401cc &``` + +1) install dependencies: + +``` +$ sudo apt-get install python-pygame libgnutls28-dev +$ pip install requests wheel slick-bitcoinrpc pygame +``` + +2) and then start: + +``` +$ git clone https://github.com/tonymorony/Komodoku +$ cd Komodoku +$ python Sudoku.py +``` diff --git a/src/gui/komodoku/Roboto-Light.ttf b/src/gui/komodoku/Roboto-Light.ttf new file mode 100755 index 000000000..664e1b2f9 Binary files /dev/null and b/src/gui/komodoku/Roboto-Light.ttf differ diff --git a/src/gui/komodoku/Sudoku.py b/src/gui/komodoku/Sudoku.py new file mode 100644 index 000000000..7b46d93fc --- /dev/null +++ b/src/gui/komodoku/Sudoku.py @@ -0,0 +1,362 @@ +#!/usr/bin/env python + +# Copyright (C) 2010 Paul Bourke +# Copyright (C) 2019 Anton Lysakov +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +import pygame +import sys +import random +import sudoku_kmdlib +import time + +class PyGameBoard(): + """Represents the game's frontend using pygame""" + + def __init__(self, engine, windowSize, gridValues, timestampValues): + pygame.init() + pygame.display.set_caption('Sudoku') + self.__engine = engine + self.__gridValues = gridValues + self.__timestampValues = timestampValues + self.__screen = pygame.display.set_mode(windowSize) + background = pygame.image.load(sys.path[0] + '/background.png').convert() + board = pygame.image.load(sys.path[0] + '/board.png') + boardX = boardY = 10 + self.__screen.blit(background, (0, 0)) + self.__screen.blit(board, (boardX, boardY)) + self.__tiles = self.__createTiles(boardX, boardY) + self.__drawUI() + self.__draw() + + def __draw(self): + """Handles events and updates display buffer""" + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + elif event.type == pygame.MOUSEBUTTONUP and event.button == 1: + self.__handleMouse(event.pos) + elif (event.type == pygame.KEYUP): + self.__handleKeyboard(event.key) + pygame.display.flip() + + def __drawUI(self): + '''Draws the text buttons along the right panel''' + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 28) + font.set_underline(True) + self.__titleText = font.render('Sudoku', 1, (0, 0, 0)) + self.__titleTextRect = self.__titleText.get_rect() + self.__titleTextRect.centerx = 445 + self.__titleTextRect.centery = 30 + self.__screen.blit(self.__titleText, self.__titleTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 14) + self.__titleText = font.render('TonyL 2019', 1, (0, 0, 0)) + self.__titleTextRect = self.__titleText.get_rect() + self.__titleTextRect.centerx = 445 + self.__titleTextRect.centery = 55 + self.__screen.blit(self.__titleText, self.__titleTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + self.__newGameText = font.render('-New Game-', 1, (0, 0, 0)) + self.__newGameTextRect = self.__newGameText.get_rect() + self.__newGameTextRect.centerx = 495 + self.__newGameTextRect.centery = 180 + self.__screen.blit(self.__newGameText, self.__newGameTextRect) + + self.__solveText = font.render('-Check Balance-', 1, (0, 0, 0)) + self.__solveTextRect = self.__solveText.get_rect() + self.__solveTextRect.centerx = 495 + self.__solveTextRect.centery = 220 + self.__screen.blit(self.__solveText, self.__solveTextRect) + + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + self.__checkText = font.render('-Check Solution-', 1, (0, 0, 0)) + self.__checkTextRect = self.__checkText.get_rect() + self.__checkTextRect.centerx = 495 + self.__checkTextRect.centery = 260 + self.__screen.blit(self.__checkText, self.__checkTextRect) + + def __handleKeyboard(self, key): + """Get key pressed and update the game board""" + validKeys = {pygame.K_0: "0", pygame.K_1: "1", pygame.K_2: "2", + pygame.K_3: "3", pygame.K_4: "4", pygame.K_5: "5", + pygame.K_6: "6", pygame.K_7: "7", pygame.K_8: "8", + pygame.K_9: "9", pygame.K_BACKSPACE: "", pygame.K_DELETE: ""} + if key == pygame.K_ESCAPE: + sys.exit() + elif key in validKeys: + i = self.__currentTile.getGridLoc()[0] + j = self.__currentTile.getGridLoc()[1] + cell_num = 9 * i + (j + 1) + self.__currentTile.setFontColor(pygame.color.THECOLORS['blue']) + self.__currentTile.updateValue(validKeys[key]) + self.__gridValues[i][j] = self.__currentTile.getValue() + self.__timestampValues[cell_num] = int(round(time.time())) + + def __handleMouse(self, (x, y)): + for row in self.__tiles: + for tile in row: + if tile.getRect().collidepoint(x, y): + if not tile.isReadOnly(): + tile.highlight(pygame.color.THECOLORS['lightyellow']) + if self.__currentTile.isCorrect(): + self.__currentTile.unhighlight() + else: + self.__currentTile.highlight((255, 164, 164)) + self.__currentTile = tile + if self.__newGameTextRect.collidepoint(x, y): + self.__engine.startNewGame() + elif self.__solveTextRect.collidepoint(x, y): + self.__engine.getSolution() + elif self.__checkTextRect.collidepoint(x, y): + ret = self.__engine.checkSolution(self.__gridValues, self.__timestampValues) + + def __updateBoard(self, gridValues): + for i in range(9): + for j in range(9): + self.__tiles[i][j].updateValue(gridValues[i][j]) + + def __unhightlightBoard(self): + for i in range(9): + for j in range(9): + self.__tiles[i][j].unhighlight() + + def __createTiles(self, initX=0, initY=0): + """Set up a list of tiles corresponding to the grid, along with + each ones location coordinates on the board""" + square_size = 40 + tiles = list() + x = y = 0 + for i in range(0, 9): + row = list() + for j in range(0, 9): + if j in (0, 1, 2): + x = (j * 41) + (initX + 2) + if j in (3, 4, 5): + x = (j * 41) + (initX + 6) + if j in (6, 7, 8): + x = (j * 41) + (initX + 10) + if i in (0, 1, 2): + y = (i * 41) + (initY + 2) + if i in (3, 4, 5): + y = (i * 41) + (initY + 6) + if i in (6, 7, 8): + y = (i * 41) + (initY + 10) + tile = Tile(self.__gridValues[i][j], (x, y), (i, j), square_size) + row.append(tile) + tiles.append(row) + self.__currentTile = tiles[0][0] + return tiles + + +class Tile(): + """Represents a graphical tile on the board""" + + def __init__(self, value, coords, gridLoc, size): + xpos = coords[0] + ypos = coords[1] + self.__fontColor = pygame.color.THECOLORS["black"] + self.__readOnly = False + self.__colorSquare = pygame.Surface((size, size)).convert() + self.__colorSquare.fill(pygame.color.THECOLORS['white'], None, pygame.BLEND_RGB_ADD) + self.__colorSquareRect = self.__colorSquare.get_rect() + self.__colorSquareRect = self.__colorSquareRect.move(xpos + 1, ypos + 1) + self.__value = value + self.__gridLoc = gridLoc + self.__screen = pygame.display.get_surface() + self.__rect = pygame.Rect(xpos, ypos, size, size) + self.__isCorrect = True + if self.__value is not '-': + self.__readOnly = True + self.__draw() + + def updateValue(self, value): + self.__value = value + self.__draw() + + def isCorrect(self): + return self.__isCorrect + + def setCorrect(self, isCorrect): + self.__isCorrect = isCorrect + + def setFontColor(self, fontColor): + self.__fontColor = fontColor + + def getValue(self): + return self.__value + + def getRect(self): + return self.__rect + + def getGridLoc(self): + return self.__gridLoc + + def isReadOnly(self): + return self.__readOnly + + def highlight(self, color): + if self.__readOnly is True: + return + self.__colorSquare.fill(color) + self.__draw() + + def unhighlight(self): + self.__colorSquare.fill((255, 225, 255), None, pygame.BLEND_RGB_ADD) + self.__draw() + + def __draw(self): + value = self.__value + if self.__value == '-': + value = '' + font = pygame.font.Font(sys.path[0] + '/Roboto-Light.ttf', 24) + text = font.render(str(value), 1, self.__fontColor) + textpos = text.get_rect() + textpos.centerx = self.__rect.centerx + textpos.centery = self.__rect.centery + self.__screen.blit(self.__colorSquare, self.__colorSquareRect) + self.__screen.blit(text, textpos) + + +class Sudoku: + """Represents the game's backend and logic""" + + def __init__(self, puzzleFile, rpc_connection): + self.__puzzleFile = puzzleFile + self.__rpc_connection = rpc_connection + self.startNewGame() + + def startNewGame(self): + self.__linePuzzle = self.__loadPuzzle(self.__puzzleFile) + gridValues = self.lineToGrid(self.__linePuzzle) + # prefill 0 timestamps for already known numbers + timestampValues = self.prefill_timestamps(gridValues) + board = PyGameBoard(self, (600, 400), gridValues, timestampValues) + board.setValues(gridValues) + + def __loadPuzzle(self, listName): + self.__chosen_puzzle = random.choice(listName) + puzzle = self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["unsolved"] + print "Puzzle ID: " + self.__chosen_puzzle + print "Reward amount: " + str(self.__rpc_connection.cclib("txidinfo", "17", '"%22' + self.__chosen_puzzle + '%22"')["amount"]) + ret = [] + linePuzzle = str(puzzle) + for i in linePuzzle: + ret.append(i) + return ret + + def gridToLine(self, grid): + linePuzzle = '' + for i in range(9): + for j in range(9): + linePuzzle += grid[i][j] + return linePuzzle + + def lineToGrid(self, linePuzzle): + assert (len(linePuzzle) == 81) + grid = [] + for i in xrange(0, 81, 9): + grid.append(linePuzzle[i:i + 9]) + return grid + + def getSolution(self): + balance = self.__rpc_connection.cclibaddress("17")["mybalance"] + print "Your balance: " + str(balance) + + def __solve(self, linePuzzle): + linePuzzle = ''.join(linePuzzle) + i = linePuzzle.find('-') + if i == -1: + return linePuzzle + + excluded_numbers = set() + for j in range(81): + if self.sameRow(i, j) or self.sameCol(i, j) or self.sameBlock(i, j): + excluded_numbers.add(linePuzzle[j]) + + for m in '123456789': + if m not in excluded_numbers: + funcRet = self.__solve(linePuzzle[:i] + m + linePuzzle[i + 1:]) + if funcRet is not None: + return funcRet + + def prefill_timestamps(self, grid): + timestamps = {} + for i in range(9): + for j in range(9): + if grid[i][j] != '-': + cell_num = 9 * i + ( j + 1 ) + timestamps[cell_num] = 0 + return timestamps + + def sameRow(self, i, j): + return (i / 9 == j / 9) + + def sameCol(self, i, j): + return (i - j) % 9 == 0 + + def sameBlock(self, i, j): + return (i / 27 == j / 27 and i % 9 / 3 == j % 9 / 3) + + def checkSolution(self, attemptGrid, timestampValues): + # [%22%22,%22%22,t0,t1,t2,...] + attemptLine = self.gridToLine(attemptGrid) + + #print attemptLine + #print timestampValues + timestampsline = "" + for timestamp in timestampValues.values(): + timestampsline += "," + timestampsline += str(timestamp) + arg_line = "[%22"+self.__chosen_puzzle+"%22,%22"+attemptLine+"%22"+timestampsline+"]" + print arg_line + try: + solution_info = self.__rpc_connection.cclib("solution", "17", '"' + arg_line + '"') + print solution_info + solution_txid = self.__rpc_connection.sendrawtransaction(solution_info["hex"]) + print "Solution accepted!" + print solution_txid + except Exception as e: + print(e) + print(solution_info) + solution_txid = 'error' + return solution_txid + +def main(): + while True: + # Assetchain hardcoded here + chain = 'SUDOKU' + try: + print 'Welcome to the Komodo SudokuCC' + rpc_connection = sudoku_kmdlib.def_credentials(chain) + pending_puzzles = rpc_connection.cclib("pending", "17")["pending"] + puzzle_list = [] + for puzzle in pending_puzzles: + puzzle_list.append(puzzle["txid"]) + + except Exception as e: + #print rpc_connection + print e + print 'Cant connect to SUDOKU Daemon! Please re-check if it up' + sys.exit() + else: + print 'Succesfully connected!\n' + break + newGame = Sudoku(puzzle_list, rpc_connection) + +if __name__ == '__main__': + main() diff --git a/src/gui/komodoku/background.png b/src/gui/komodoku/background.png new file mode 100644 index 000000000..dc4844a0b Binary files /dev/null and b/src/gui/komodoku/background.png differ diff --git a/src/gui/komodoku/board.png b/src/gui/komodoku/board.png new file mode 100644 index 000000000..bd77ca419 Binary files /dev/null and b/src/gui/komodoku/board.png differ diff --git a/src/gui/komodoku/sudoku_kmdlib.py b/src/gui/komodoku/sudoku_kmdlib.py new file mode 100644 index 000000000..a2b2aa239 --- /dev/null +++ b/src/gui/komodoku/sudoku_kmdlib.py @@ -0,0 +1,41 @@ +import platform +import os +import re +import random +from slickrpc import Proxy + + +# define function that fetchs rpc creds from .conf +def def_credentials(chain): + rpcport =''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check "+coin_config_file) + exit(1) + + return(Proxy("http://%s:%s@127.0.0.1:%d"%(rpcuser, rpcpassword, int(rpcport)))) + + diff --git a/src/importcoin.cpp b/src/importcoin.cpp index 39ff01d68..051129020 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -26,14 +26,18 @@ int32_t komodo_nextheight(); -CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector payouts) +CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride) { std::vector payload = E_MARSHAL(ss << EVAL_IMPORTCOIN); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + if (mtx.fOverwintered) + mtx.nExpiryHeight = 0; mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload)); mtx.vout = payouts; auto importData = E_MARSHAL(ss << proof; ss << burnTx); - mtx.vout.insert(mtx.vout.begin(), CTxOut(0, CScript() << OP_RETURN << importData)); + mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << importData)); + if (nExpiryHeightOverride != 0) + mtx.nExpiryHeight = nExpiryHeightOverride; //this is for construction of the tx used for validating importtx return CTransaction(mtx); } @@ -48,39 +52,97 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb return CTxOut(value, CScript() << OP_RETURN << opret); } +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof, + uint256 bindtxid,std::vector publishers,std::vector txids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub) +{ + std::vector opret; + opret = E_MARSHAL(ss << VARINT(targetCCid); + ss << targetSymbol; + ss << SerializeHash(payouts); + ss << rawproof; + ss << bindtxid; + ss << publishers; + ss << txids; + ss << height; + ss << burnvout; + ss << rawburntx; + ss << destpub); + + return CTxOut(value, CScript() << OP_RETURN << opret); +} -bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx, +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof,std::string srcaddr, + std::string receipt) +{ + std::vector opret; + opret = E_MARSHAL(ss << VARINT(targetCCid); + ss << targetSymbol; + ss << SerializeHash(payouts); + ss << rawproof; + ss << srcaddr; + ss << receipt); + return CTxOut(value, CScript() << OP_RETURN << opret); +} + + +bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx, std::vector &payouts) { std::vector vData; - GetOpReturnData(importTx.vout[0].scriptPubKey, vData); + GetOpReturnData(importTx.vout[importTx.vout.size()-1].scriptPubKey, vData); if (importTx.vout.size() < 1) return false; - payouts = std::vector(importTx.vout.begin()+1, importTx.vout.end()); + payouts = std::vector(importTx.vout.begin(), importTx.vout.end()-1); return importTx.vin.size() == 1 && importTx.vin[0].scriptSig == (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN)) && E_UNMARSHAL(vData, ss >> proof; ss >> burnTx); } -bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector&rawproof) +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector&rawproof) { - std::vector burnOpret; uint32_t ccid = 0; + std::vector burnOpret; uint32_t ccid = 0; bool isEof=true; + if (burnTx.vout.size() == 0) return false; GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); - E_UNMARSHAL(burnOpret, ss >> VARINT(ccid)); - /*if ( ccid != 0xffffffff ) - { - return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid); - ss >> targetSymbol; - ss >> payoutsHash); - } - else*/ - { - return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid); - ss >> targetSymbol; - ss >> payoutsHash; - ss >> rawproof); - } + return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid); + ss >> targetSymbol; + ss >> payoutsHash; + ss >> rawproof; isEof=ss.eof();) || !isEof; +} + +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt) +{ + std::vector burnOpret,rawproof; bool isEof=true; + std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash; + + if (burnTx.vout.size() == 0) return false; + GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); + return (E_UNMARSHAL(burnOpret, ss >> VARINT(targetCCid); + ss >> targetSymbol; + ss >> payoutsHash; + ss >> rawproof; + ss >> srcaddr; + ss >> receipt)); +} + +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector &publishers,std::vector &txids,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub) +{ + std::vector burnOpret,rawproof; bool isEof=true; + uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol; + + if (burnTx.vout.size() == 0) return false; + GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret); + return (E_UNMARSHAL(burnOpret, ss >> VARINT(targetCCid); + ss >> targetSymbol; + ss >> payoutsHash; + ss >> rawproof; + ss >> bindtxid; + ss >> publishers; + ss >> txids; + ss >> height; + ss >> burnvout; + ss >> rawburntx; + ss >> destpub)); } @@ -108,7 +170,7 @@ bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& che auto pc = scriptSig.begin(); opcodetype opcode; std::vector evalScript; - + auto f = [&] () { if (!scriptSig.GetOp(pc, opcode, evalScript)) return false; diff --git a/src/importcoin.h b/src/importcoin.h index 64019ac8f..8cb8dbc58 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -26,13 +26,18 @@ CAmount GetCoinImportValue(const CTransaction &tx); CTransaction MakeImportCoinTransaction(const TxProof proof, - const CTransaction burnTx, const std::vector payouts); + const CTransaction burnTx, const std::vector payouts, uint32_t nExpiryHeightOverride = 0); CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof); +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof, + uint256 bindtxid,std::vector publishers,std::vectortxids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub); +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts,std::vector rawproof,std::string srcaddr, + std::string receipt); -bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector &rawproof); -bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx, - std::vector &payouts); +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector &rawproof); +bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt); +bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector &publishers,std::vector &txids,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub); +bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx,std::vector &payouts); bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); diff --git a/src/init.cpp b/src/init.cpp index 3dd09ac50..4003f9ced 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -367,6 +367,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); strUsage += HelpMessageOpt("-checkblocks=", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), 288)); strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), 3)); + strUsage += HelpMessageOpt("-clientname=", _("Full node client name, default 'MagicBean'")); strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), "komodo.conf")); if (mode == HMM_BITCOIND) { @@ -450,6 +451,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-wallet=", _("Specify wallet file (within data directory)") + " " + strprintf(_("(default: %s)"), "wallet.dat")); strUsage += HelpMessageOpt("-walletbroadcast", _("Make the wallet broadcast transactions") + " " + strprintf(_("(default: %u)"), true)); strUsage += HelpMessageOpt("-walletnotify=", _("Execute command when a wallet transaction changes (%s in cmd is replaced by TxID)")); + strUsage += HelpMessageOpt("-whitelistaddress=", _("Enable the wallet filter for notary nodes and add one Raddress to the whitelist of the wallet filter. If -whitelistaddress= is used, then the wallet filter is automatically activated. Several Raddresses can be defined using several -whitelistaddress= (similar to -addnode). The wallet filter will filter the utxo to only ones coming from my own Raddress (derived from pubkey) and each Raddress defined using -whitelistaddress= this option is mostly for Notary Nodes).")); strUsage += HelpMessageOpt("-zapwallettxes=", _("Delete all wallet transactions and only recover those parts of the blockchain through -rescan on startup") + " " + _("(1 = keep tx meta data e.g. account owner and payment request information, 2 = drop tx meta data)")); #endif @@ -562,6 +564,36 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-metricsui", _("Set to 1 for a persistent metrics screen, 0 for sequential metrics output (default: 1 if running in a console, 0 otherwise)")); strUsage += HelpMessageOpt("-metricsrefreshtime", strprintf(_("Number of seconds between metrics refreshes (default: %u if running in a console, %u otherwise)"), 1, 600)); } + strUsage += HelpMessageGroup(_("Komodo Asset Chain options:")); + strUsage += HelpMessageOpt("-ac_algo", _("Choose PoW mining algorithm, default is Equihash")); + strUsage += HelpMessageOpt("-ac_blocktime", _("Block time in seconds, default is 60")); + strUsage += HelpMessageOpt("-ac_cc", _("Cryptoconditions, default 0")); + strUsage += HelpMessageOpt("-ac_beam", _("BEAM integration")); + strUsage += HelpMessageOpt("-ac_coda", _("CODA integration")); + strUsage += HelpMessageOpt("-ac_cclib", _("Cryptoconditions dynamicly loadable library")); + strUsage += HelpMessageOpt("-ac_ccenable", _("Cryptoconditions to enable")); + strUsage += HelpMessageOpt("-ac_ccactivate", _("Block height to enable Cryptoconditions")); + strUsage += HelpMessageOpt("-ac_decay", _("Percentage of block reward decrease at each halving")); + strUsage += HelpMessageOpt("-ac_end", _("Block height at which block rewards will end")); + strUsage += HelpMessageOpt("-ac_eras", _("Block reward eras")); + strUsage += HelpMessageOpt("-ac_founders", _("Number of blocks between founders reward payouts")); + strUsage += HelpMessageOpt("-ac_halving", _("Number of blocks between each block reward halving")); + strUsage += HelpMessageOpt("-ac_name", _("Name of asset chain")); + strUsage += HelpMessageOpt("-ac_notarypay", _("Pay notaries, default 0")); + strUsage += HelpMessageOpt("-ac_perc", _("Percentage of block rewards paid to the founder")); + strUsage += HelpMessageOpt("-ac_private", _("Shielded transactions only (except coinbase + notaries), default is 0")); + strUsage += HelpMessageOpt("-ac_pubkey", _("Public key for receiving payments on the network")); + strUsage += HelpMessageOpt("-ac_public", _("Transparent transactions only, default 0")); + strUsage += HelpMessageOpt("-ac_reward", _("Block reward in satoshis, default is 0")); + strUsage += HelpMessageOpt("-ac_sapling", _("Sapling activation block height")); + strUsage += HelpMessageOpt("-ac_script", _("P2SH/multisig address to receive founders rewards")); + strUsage += HelpMessageOpt("-ac_staked", _("Percentage of blocks that are Proof-Of-Stake, default 0")); + strUsage += HelpMessageOpt("-ac_supply", _("Starting supply, default is 0")); + strUsage += HelpMessageOpt("-ac_timelockfrom", _("Timelocked coinbase start height")); + strUsage += HelpMessageOpt("-ac_timelockgte", _("Timelocked coinbase minimum amount to be locked")); + strUsage += HelpMessageOpt("-ac_timelockto", _("Timelocked coinbase stop height")); + strUsage += HelpMessageOpt("-ac_txpow", _("Enforce transaction-rate limit, default 0")); + strUsage += HelpMessageOpt("-ac_veruspos", _("Use Verus Proof-Of-Stake (-ac_veruspos=50) default 0")); return strUsage; } @@ -1177,7 +1209,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) globalVerifyHandle.reset(new ECCVerifyHandle()); // set the hash algorithm to use for this chain - extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH; + // Again likely better solution here, than using long IF ELSE. + extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_VERUSHASHV1_1; CVerusHash::init(); CVerusHashV2::init(); if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH) @@ -1185,6 +1218,11 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) // initialize VerusHash CBlockHeader::SetVerusHash(); } + else if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1) + { + // initialize VerusHashV2 + CBlockHeader::SetVerusHashV2(); + } // Sanity check if (!InitSanityCheck()) @@ -1548,6 +1586,8 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) if (fReindex) { + boost::filesystem::remove(GetDataDir() / "komodostate"); + boost::filesystem::remove(GetDataDir() / "signedmasks"); pblocktree->WriteReindexing(true); //If we're reindexing in prune mode, wipe away unusable block files and all undo data files if (fPruneMode) @@ -1847,7 +1887,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) uiInterface.InitMessage(_("Activating best chain...")); // scan for better chains in the block chain database, that are not yet connected in the active best chain CValidationState state; - if ( !ActivateBestChain(state)) + if ( !ActivateBestChain(true,state)) strErrors << "Failed to connect best block"; } std::vector vImportFiles; diff --git a/src/key.cpp b/src/key.cpp index 3b6fed81b..6e2370dc2 100644 --- a/src/key.cpp +++ b/src/key.cpp @@ -179,6 +179,22 @@ void CKey::MakeNewKey(bool fCompressedIn) { fCompressed = fCompressedIn; } +int32_t CKey::SetKey32(uint8_t Key32[32]) +{ + memcpy(vch,Key32,32); + fCompressed = true; + if ( Check(vch) == 0 ) + { + fValid = false; + return(-1); + } + else + { + fValid = true; + return(0); + } +} + bool CKey::SetPrivKey(const CPrivKey &privkey, bool fCompressedIn) { if (!ec_privkey_import_der(secp256k1_context_sign, (unsigned char*)begin(), &privkey[0], privkey.size())) return false; diff --git a/src/key.h b/src/key.h index c41208003..857e8a8ae 100644 --- a/src/key.h +++ b/src/key.h @@ -128,6 +128,7 @@ public: //! Generate a new private key using a cryptographic PRNG. void MakeNewKey(bool fCompressed); + int32_t SetKey32(uint8_t Key32[32]); /** * Convert the private key to a CPrivKey (serialized OpenSSL private key data). diff --git a/src/key_io.cpp b/src/key_io.cpp index f04c4da04..1953d9623 100644 --- a/src/key_io.cpp +++ b/src/key_io.cpp @@ -206,6 +206,19 @@ std::string EncodeSecret(const CKey& key) return ret; } +std::string EncodeCustomSecret(const CKey& key,uint8_t secret_key) +{ + assert(key.IsValid()); + std::vector data = std::vector(1,secret_key);; + data.insert(data.end(), key.begin(), key.end()); + if (key.IsCompressed()) { + data.push_back(1); + } + std::string ret = EncodeBase58Check(data); + memory_cleanse(data.data(), data.size()); + return ret; +} + CExtPubKey DecodeExtPubKey(const std::string& str) { CExtPubKey key; diff --git a/src/key_io.h b/src/key_io.h index 3606ad09f..72823d57e 100644 --- a/src/key_io.h +++ b/src/key_io.h @@ -18,6 +18,7 @@ CKey DecodeSecret(const std::string& str); std::string EncodeSecret(const CKey& key); +std::string EncodeCustomSecret(const CKey& key,uint8_t secret_key); CExtKey DecodeExtKey(const std::string& str); std::string EncodeExtKey(const CExtKey& extkey); diff --git a/src/komodo.h b/src/komodo.h index d3cda4e22..15c1e5c02 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -16,6 +16,7 @@ #ifndef H_KOMODO_H #define H_KOMODO_H #include "komodo_defs.h" +#include "notaries_staked.h" #ifdef _WIN32 #define printf(...) @@ -26,6 +27,8 @@ #define KOMODO_ASSETCHAINS_WAITNOTARIZE #define KOMODO_PAXMAX (10000 * COIN) +extern int32_t NOTARIZED_HEIGHT; +uint256 NOTARIZED_HASH,NOTARIZED_DESTTXID; #include #include @@ -36,7 +39,8 @@ int32_t gettxout_scriptPubKey(uint8_t *scriptPubkey,int32_t maxsize,uint256 txid,int32_t n); void komodo_event_rewind(struct komodo_state *sp,char *symbol,int32_t height); -void komodo_connectblock(CBlockIndex *pindex,CBlock& block); +int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block); +bool check_pprevnotarizedht(); #include "komodo_structs.h" #include "komodo_globals.h" @@ -530,7 +534,7 @@ int32_t komodo_validate_chain(uint256 srchash,int32_t notarized_height) } else return(1); } -int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen,int32_t height,uint256 txhash,int32_t i,int32_t j,uint64_t *voutmaskp,int32_t *specialtxp,int32_t *notarizedheightp,uint64_t value,int32_t notarized,uint64_t signedmask,uint32_t timestamp) +int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen,int32_t height,uint256 txhash,int32_t i,int32_t j,uint64_t *voutmaskp,int32_t *specialtxp,int32_t *notarizedheightp,uint64_t value,int32_t notarized,uint64_t signedmask,uint32_t timestamp) { static uint256 zero; static FILE *signedfp; int32_t opretlen,nid,offset,k,MoMdepth,matched,len = 0; uint256 MoM,srchash,desttxid; uint8_t crypto777[33]; struct komodo_state *sp; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; @@ -615,7 +619,7 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr if ( j == 1 && opretlen >= len+offset-opoffset ) { memset(&MoMoMdata,0,sizeof(MoMoMdata)); - if ( matched == 0 && bitweight(signedmask) >= KOMODO_MINRATIFY ) + if ( matched == 0 && signedmask != 0 && bitweight(signedmask) >= KOMODO_MINRATIFY ) notarized = 1; if ( strcmp("PIZZA",ccdata.symbol) == 0 || strncmp("TXSCL",ccdata.symbol,5) == 0 ) notarized = 1; @@ -628,6 +632,9 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr if ( matched != 0 ) validated = komodo_validate_chain(srchash,*notarizedheightp); else validated = 1; + // Any notarization that is matched and has a decodable op_return is enough to pay notaries. Otherwise bugs! + if ( fJustCheck && matched != 0 ) + return(-2); if ( notarized != 0 && validated != 0 ) { //sp->NOTARIZED_HEIGHT = *notarizedheightp; @@ -694,6 +701,7 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr } else if ( ASSETCHAINS_SYMBOL[0] == 0 && matched != 0 && notarized != 0 && validated != 0 ) komodo_rwccdata((char *)"KMD",1,&ccdata,0); + if ( matched != 0 && *notarizedheightp > sp->NOTARIZED_HEIGHT && *notarizedheightp < height ) { sp->NOTARIZED_HEIGHT = *notarizedheightp; @@ -706,11 +714,8 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr } komodo_stateupdate(height,0,0,0,zero,0,0,0,0,0,0,0,0,0,0,sp->MoM,sp->MoMdepth); if ( ASSETCHAINS_SYMBOL[0] != 0 ) - printf("[%s] ht.%d NOTARIZED.%d %s.%s %sTXID.%s lens.(%d %d) MoM.%s %d\n",ASSETCHAINS_SYMBOL,height,*notarizedheightp,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,srchash.ToString().c_str(),ASSETCHAINS_SYMBOL[0]==0?"BTC":"KMD",desttxid.ToString().c_str(),opretlen,len,sp->MoM.ToString().c_str(),sp->MoMdepth); - if ( 0 && RemoveOrphanedBlocks(*notarizedheightp)) - { - //fprintf(stderr, "Sucessfully removed all known orphaned blocks before height %d\n",*notarizedheightp); - } + printf("[%s] ht.%d NOTARIZED.%d %s.%s %sTXID.%s lens.(%d %d) MoM.%s %d\n",ASSETCHAINS_SYMBOL,height,sp->NOTARIZED_HEIGHT,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,srchash.ToString().c_str(),ASSETCHAINS_SYMBOL[0]==0?"BTC":"KMD",desttxid.ToString().c_str(),opretlen,len,sp->MoM.ToString().c_str(),sp->MoMdepth); + if ( ASSETCHAINS_SYMBOL[0] == 0 ) { if ( signedfp == 0 ) @@ -735,7 +740,8 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr komodo_stateupdate(height,0,0,0,txhash,0,0,0,0,0,0,value,&scriptbuf[len],opretlen-len+4+3+(scriptbuf[1] == 0x4d),j,zero,0); } } - } + } //else if ( fJustCheck ) + // return (-3); // if the notarisation is only invalid because its out of order it cannot be mined in a block with a valid one! } else if ( opretlen != 149 && height > 600000 && matched != 0 ) printf("%s validated.%d notarized.%d %llx reject ht.%d NOTARIZED.%d prev.%d %s.%s DESTTXID.%s len.%d opretlen.%d\n",ccdata.symbol,validated,notarized,(long long)signedmask,height,*notarizedheightp,sp->NOTARIZED_HEIGHT,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,srchash.ToString().c_str(),desttxid.ToString().c_str(),len,opretlen); } @@ -760,7 +766,7 @@ int32_t komodo_voutupdate(int32_t *isratificationp,int32_t notaryid,uint8_t *scr printf("ISRATIFICATION (%s)\n",(char *)&scriptbuf[len+32*2+4]); } } - + if ( *isratificationp == 0 && (signedmask != 0 || (scriptbuf[len] != 'X' && scriptbuf[len] != 'A')) ) // && scriptbuf[len] != 'I') komodo_stateupdate(height,0,0,0,txhash,0,0,0,0,0,0,value,&scriptbuf[len],opretlen,j,zero,0); } @@ -797,16 +803,19 @@ int32_t komodo_notarycmp(uint8_t *scriptPubKey,int32_t scriptlen,uint8_t pubkeys return(-1); } -void komodo_connectblock(CBlockIndex *pindex,CBlock& block) +// int32_t ! +int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) { static int32_t hwmheight; + int32_t staked_era; static int32_t lastStakedEra; + std::vector notarisations; uint64_t signedmask,voutmask; char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp; uint8_t scriptbuf[10001],pubkeys[64][33],rmd160[20],scriptPubKey[35]; uint256 zero,btctxid,txhash; int32_t i,j,k,numnotaries,notarized,scriptlen,isratification,nid,numvalid,specialtx,notarizedheight,notaryid,len,numvouts,numvins,height,txn_count; if ( pindex == 0 ) { fprintf(stderr,"komodo_connectblock null pindex\n"); - return; + return(0); } memset(&zero,0,sizeof(zero)); komodo_init(pindex->GetHeight()); @@ -814,9 +823,22 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) if ( (sp= komodo_stateptr(symbol,dest)) == 0 ) { fprintf(stderr,"unexpected null komodostateptr.[%s]\n",ASSETCHAINS_SYMBOL); - return; + return(0); + } + //fprintf(stderr,"%s connect.%d\n",ASSETCHAINS_SYMBOL,pindex->nHeight); + // Wallet Filter. Disabled here. Cant be activated by notaries or pools with some changes. + if ( is_STAKED(ASSETCHAINS_SYMBOL) != 0 || IS_STAKED_NOTARY > -1 ) + { + staked_era = STAKED_era(pindex->GetBlockTime()); + if ( !fJustCheck && staked_era != lastStakedEra ) + { + uint8_t tmp_pubkeys[64][33]; + int8_t numSN = numStakedNotaries(tmp_pubkeys,staked_era); + UpdateNotaryAddrs(tmp_pubkeys,numSN); + STAKED_ERA = staked_era; + lastStakedEra = staked_era; + } } - //fprintf(stderr,"%s connect.%d\n",ASSETCHAINS_SYMBOL,pindex->GetHeight()); numnotaries = komodo_notaries(pubkeys,pindex->GetHeight(),pindex->GetBlockTime()); calc_rmd160_sha256(rmd160,pubkeys[0],33); if ( pindex->GetHeight() > hwmheight ) @@ -829,16 +851,27 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) komodo_purge_ccdata((int32_t)pindex->GetHeight()); hwmheight = pindex->GetHeight(); } - komodo_event_rewind(sp,symbol,pindex->GetHeight()); - komodo_stateupdate(pindex->GetHeight(),0,0,0,zero,0,0,0,0,-pindex->GetHeight(),pindex->nTime,0,0,0,0,zero,0); + if (!fJustCheck) + { + komodo_event_rewind(sp,symbol,pindex->GetHeight()); + komodo_stateupdate(pindex->GetHeight(),0,0,0,zero,0,0,0,0,-pindex->GetHeight(),pindex->nTime,0,0,0,0,zero,0); + } } komodo_currentheight_set(chainActive.LastTip()->GetHeight()); + int transaction = 0; if ( pindex != 0 ) { height = pindex->GetHeight(); txn_count = block.vtx.size(); for (i=0; i 1 && ASSETCHAINS_NOTARY_PAY[0] != 0 ) + break; txhash = block.vtx[i].GetHash(); numvouts = block.vtx[i].vout.size(); notaryid = -1; @@ -863,11 +896,11 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) } //else printf("cant get scriptPubKey for ht.%d txi.%d vin.%d\n",height,i,j); } numvalid = bitweight(signedmask); - if ( (((height < 90000 || (signedmask & 1) != 0) && numvalid >= KOMODO_MINRATIFY) || + if ( ((height < 90000 || (signedmask & 1) != 0) && numvalid >= KOMODO_MINRATIFY) || (numvalid >= KOMODO_MINRATIFY && ASSETCHAINS_SYMBOL[0] != 0) || - numvalid > (numnotaries/5)) ) + numvalid > (numnotaries/5) ) { - if ( ASSETCHAINS_SYMBOL[0] != 0 ) + if ( !fJustCheck && ASSETCHAINS_SYMBOL[0] != 0) { static FILE *signedfp; if ( signedfp == 0 ) @@ -884,10 +917,18 @@ void komodo_connectblock(CBlockIndex *pindex,CBlock& block) fwrite(&signedmask,1,sizeof(signedmask),signedfp); fflush(signedfp); } + transaction = i; printf("[%s] ht.%d txi.%d signedmask.%llx numvins.%d numvouts.%d <<<<<<<<<<< notarized\n",ASSETCHAINS_SYMBOL,height,i,(long long)signedmask,numvins,numvouts); } notarized = 1; } + // simulate DPoW in regtest mode for dpowconfs tests/etc + if ( Params().NetworkIDString() == "regtest" && ( height%7 == 0) ) { + notarized = 1; + sp->NOTARIZED_HEIGHT = height; + sp->NOTARIZED_HASH = block.GetHash(); + sp->NOTARIZED_DESTTXID = txhash; + } if ( IS_KOMODO_NOTARY != 0 && ASSETCHAINS_SYMBOL[0] == 0 ) printf("(tx.%d: ",i); for (j=0; j= sizeof(uint32_t) && len <= sizeof(scriptbuf) ) { memcpy(scriptbuf,(uint8_t *)&block.vtx[i].vout[j].scriptPubKey[0],len); - notaryid = komodo_voutupdate(&isratification,notaryid,scriptbuf,len,height,txhash,i,j,&voutmask,&specialtx,¬arizedheight,(uint64_t)block.vtx[i].vout[j].nValue,notarized,signedmask,(uint32_t)chainActive.LastTip()->GetBlockTime()); + notaryid = komodo_voutupdate(fJustCheck,&isratification,notaryid,scriptbuf,len,height,txhash,i,j,&voutmask,&specialtx,¬arizedheight,(uint64_t)block.vtx[i].vout[j].nValue,notarized,signedmask,(uint32_t)chainActive.LastTip()->GetBlockTime()); + if ( fJustCheck && notaryid == -2 ) + { + // We see a valid notarisation here, save its location. + notarisations.push_back(i); + } if ( 0 && i > 0 ) { for (k=0; kGetHeight() == hwmheight ) + if ( !fJustCheck && pindex->GetHeight() == hwmheight ) komodo_stateupdate(height,0,0,0,zero,0,0,0,0,height,(uint32_t)pindex->nTime,0,0,0,0,zero,0); } else fprintf(stderr,"komodo_connectblock: unexpected null pindex\n"); //KOMODO_INITDONE = (uint32_t)time(NULL); //fprintf(stderr,"%s end connect.%d\n",ASSETCHAINS_SYMBOL,pindex->GetHeight()); + if (fJustCheck) + { + if ( notarisations.size() == 0 ) + return(0); + if ( notarisations.size() == 1 && notarisations[0] == 1 ) + return(1); + if ( notarisations.size() > 1 || (notarisations.size() == 1 && notarisations[0] != 1) ) + return(-1); + } + else return(0); } - #endif diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index c530db4fb..2f9c1a8d7 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -25,9 +25,14 @@ int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); int32_t komodo_electednotary(int32_t *numnotariesp,uint8_t *pubkey33,int32_t height,uint32_t timestamp); +int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notaryid,uint8_t *scriptbuf,int32_t scriptlen,int32_t height,uint256 txhash,int32_t i,int32_t j,uint64_t *voutmaskp,int32_t *specialtxp,int32_t *notarizedheightp,uint64_t value,int32_t notarized,uint64_t signedmask,uint32_t timestamp); unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); bool EnsureWalletIsAvailable(bool avoidException); extern bool fRequestShutdown; + +int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); +uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); + //#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"curl",(char *)"http://127.0.0.1:7776",0,0,(char *)(cmdstr)) struct MemoryStruct { char *memory; size_t size; }; @@ -52,11 +57,30 @@ void init_string(struct return_string *s) if ( s->ptr == NULL ) { fprintf(stderr,"init_string malloc() failed\n"); - exit(-1); + StartShutdown(); } s->ptr[0] = '\0'; } +int tx_height( const uint256 &hash ){ + int nHeight = 0; + CTransaction tx; + uint256 hashBlock; + if (!GetTransaction(hash, tx, hashBlock, true)) { + fprintf(stderr,"tx hash %s does not exist!\n", hash.ToString().c_str() ); + } + + BlockMap::const_iterator it = mapBlockIndex.find(hashBlock); + if (it != mapBlockIndex.end()) { + nHeight = it->second->GetHeight(); + //fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight); + } else { + fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() ); + } + return nHeight; +} + + /************************************************************************ * * Use the "writer" to accumulate text until done @@ -70,7 +94,7 @@ size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string * if ( s->ptr == NULL ) { fprintf(stderr, "accumulate realloc() failed\n"); - exit(-1); + StartShutdown(); } memcpy(s->ptr+s->len,ptr,size*nmemb); s->ptr[new_len] = '\0'; @@ -177,6 +201,9 @@ try_again: curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA, &s); // we pass our 's' struct to the callback curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL, 1L); // supposed to fix "Alarm clock" and long jump crash curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS, 1L); // no progress callback + //curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); + //curl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 2); + if ( strncmp(url,"https",5) == 0 ) { curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0); @@ -223,13 +250,13 @@ try_again: numretries++; if ( specialcase != 0 ) { - printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); + fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res); free(s.ptr); return(0); } else if ( numretries >= 1 ) { - //printf("Maximum number of retries exceeded!\n"); + fprintf(stderr,"Maximum number of retries exceeded!\n"); free(s.ptr); return(0); } @@ -344,7 +371,7 @@ char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port) { sprintf(url,(char *)"http://127.0.0.1:%u",port); sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params); - //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,KMDUSERPASS); + //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,KMDUSERPASS); retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params); //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0); } @@ -421,6 +448,18 @@ int32_t komodo_verifynotarizedscript(int32_t height,uint8_t *script,int32_t len, return(-1); } +void komodo_reconsiderblock(uint256 blockhash) +{ + char params[256],*jsonstr,*hexstr; + sprintf(params,"[\"%s\"]",blockhash.ToString().c_str()); + if ( (jsonstr= komodo_issuemethod(ASSETCHAINS_USERPASS,(char *)"reconsiderblock",params,ASSETCHAINS_RPCPORT)) != 0 ) + { + //fprintf(stderr,"komodo_reconsiderblock.(%s) (%s %u) -> (%s)\n",params,ASSETCHAINS_USERPASS,ASSETCHAINS_RPCPORT,jsonstr); + free(jsonstr); + } + //fprintf(stderr,"komodo_reconsiderblock.(%s) (%s %u) -> NULL\n",params,ASSETCHAINS_USERPASS,ASSETCHAINS_RPCPORT); +} + int32_t komodo_verifynotarization(char *symbol,char *dest,int32_t height,int32_t NOTARIZED_HEIGHT,uint256 NOTARIZED_HASH,uint256 NOTARIZED_DESTTXID) { char params[256],*jsonstr,*hexstr; uint8_t *script,_script[8192]; int32_t n,len,retval = -1; cJSON *json,*txjson,*vouts,*vout,*skey; @@ -1033,6 +1072,25 @@ int32_t komodo_MoM(int32_t *notarized_heightp,uint256 *MoMp,uint256 *kmdtxidp,in return(depth); } +CBlockIndex *komodo_blockindex(uint256 hash) +{ + BlockMap::const_iterator it; CBlockIndex *pindex = 0; + if ( (it = mapBlockIndex.find(hash)) != mapBlockIndex.end() ) + pindex = it->second; + return(pindex); +} + +int32_t komodo_blockheight(uint256 hash) +{ + BlockMap::const_iterator it; CBlockIndex *pindex = 0; + if ( (it = mapBlockIndex.find(hash)) != mapBlockIndex.end() ) + { + if ( (pindex= it->second) != 0 ) + return(pindex->GetHeight()); + } + return(0); +} + int32_t komodo_checkpoint(int32_t *notarized_heightp,int32_t nHeight,uint256 hash) { int32_t notarized_height,MoMdepth; uint256 MoM,notarized_hash,notarized_desttxid; CBlockIndex *notary,*pindex; @@ -1108,10 +1166,10 @@ uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 int32_t komodo_nextheight() { - CBlockIndex *pindex; int32_t ht,longest = komodo_longestchain(); - if ( (pindex= chainActive.LastTip()) != 0 && (ht= pindex->GetHeight()) >= longest ) + CBlockIndex *pindex; int32_t ht; + if ( (pindex= chainActive.LastTip()) != 0 && (ht= pindex->GetHeight()) > 0 ) return(ht+1); - else return(longest + 1); + else return(komodo_longestchain() + 1); } int32_t komodo_isrealtime(int32_t *kmdheightp) @@ -1161,6 +1219,10 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); uint64_t komodo_commission(const CBlock *pblock,int32_t height) { + // LABS fungible chains, cannot have any block reward! + if ( is_STAKED(ASSETCHAINS_SYMBOL) == 2 ) + return(0); + int32_t i,j,n=0,txn_count; int64_t nSubsidy; uint64_t commission,total = 0; txn_count = pblock->vtx.size(); if ( ASSETCHAINS_FOUNDERS != 0 ) @@ -1171,7 +1233,12 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height) if ( ASSETCHAINS_FOUNDERS > 1 ) { if ( (height % ASSETCHAINS_FOUNDERS) == 0 ) - commission = commission * ASSETCHAINS_FOUNDERS; + { + if ( ASSETCHAINS_FOUNDERS_REWARD == 0 ) + commission = commission * ASSETCHAINS_FOUNDERS; + else + commission = ASSETCHAINS_FOUNDERS_REWARD; + } else commission = 0; } } @@ -1182,6 +1249,8 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height) n = pblock->vtx[i].vout.size(); for (j=0; j 225000 && ASSETCHAINS_STAKED != 0 && txn_count > 1 && i == txn_count-1 && j == n-1 ) + break; //fprintf(stderr,"(%d %.8f).%d ",i,dstr(pblock->vtx[i].vout[j].nValue),j); if ( i != 0 || j != 1 ) total += pblock->vtx[i].vout[j].nValue; @@ -1229,7 +1298,8 @@ int8_t komodo_segid(int32_t nocache,int32_t height) if ( strcmp(destaddr,voutaddr) == 0 && block.vtx[txn_count-1].vout[0].nValue == value ) { segid = komodo_segid32(voutaddr) & 0x3f; - //fprintf(stderr,"komodo_segid.(%d) -> %02x\n",height,segid); + pindex->segid = segid; + //fprintf(stderr,"komodo_segid.(%d) -> %d\n",height,pindex->segid); } } else fprintf(stderr,"komodo_segid ht.%d couldnt extract voutaddress\n",height); } @@ -1272,100 +1342,18 @@ uint32_t komodo_stakehash(uint256 *hashp,char *address,uint8_t *hashbuf,uint256 return(addrhash.uints[0]); } -uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 txid,int32_t vout,uint32_t blocktime,uint32_t prevtime,char *destaddr) -{ - bool fNegative,fOverflow; uint8_t hashbuf[256]; char address[64]; bits256 addrhash; arith_uint256 hashval,mindiff,ratio,coinage256; uint256 hash,pasthash; int32_t diff=0,segid,minage,i,iter=0; uint32_t txtime,segid32,winner = 0 ; uint64_t value,coinage; - txtime = komodo_txtime2(&value,txid,vout,address); - if ( validateflag == 0 ) - { - //fprintf(stderr,"blocktime.%u -> ",blocktime); - if ( blocktime < prevtime+3 ) - blocktime = prevtime+3; - if ( blocktime < GetAdjustedTime()-60 ) - blocktime = GetAdjustedTime()+30; - //fprintf(stderr,"blocktime.%u txtime.%u\n",blocktime,txtime); - } - if ( value == 0 || txtime == 0 || blocktime == 0 || prevtime == 0 ) - { - //fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime); - return(0); - } - if ( value < SATOSHIDEN ) - return(0); - value /= SATOSHIDEN; - mindiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); - ratio = (mindiff / bnTarget); - if ( (minage= nHeight*3) > 6000 ) // about 100 blocks - minage = 6000; - komodo_segids(hashbuf,nHeight-101,100); - segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout); - segid = ((nHeight + segid32) & 0x3f); - for (iter=0; iter<600; iter++) - { - if ( blocktime+iter+segid*2 < txtime+minage ) - continue; - diff = (iter + blocktime - txtime - minage); - if ( diff < 0 ) - diff = 60; - else if ( diff > 3600*24*30 ) - { - //printf("diff.%d (iter.%d blocktime.%u txtime.%u minage.%d)\n",(int32_t)diff,iter,blocktime,txtime,(int32_t)minage); - diff = 3600*24*30; - } - if ( iter > 0 ) - diff += segid*2; - coinage = (value * diff); - if ( blocktime+iter+segid*2 > prevtime+480 ) - coinage *= ((blocktime+iter+segid*2) - (prevtime+400)); - coinage256 = arith_uint256(coinage+1); - hashval = ratio * (UintToArith256(hash) / coinage256); - if ( hashval <= bnTarget ) - { - winner = 1; - if ( validateflag == 0 ) - { - //fprintf(stderr,"winner blocktime.%u iter.%d segid.%d\n",blocktime,iter,segid); - blocktime += iter; - blocktime += segid * 2; - } - break; - } - if ( validateflag != 0 ) - { - /*for (i=31; i>=24; i--) - fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); - fprintf(stderr," vs "); - for (i=31; i>=24; i--) - fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); - fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff);*/ - break; - } - } - //fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner); - if ( 0 && validateflag != 0 ) - { - for (i=31; i>=24; i--) - fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); - fprintf(stderr," vs "); - for (i=31; i>=24; i--) - fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); - fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d ht.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff,nHeight); - } - if ( nHeight < 10 ) - return(blocktime); - return(blocktime * winner); -} - arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc) { int32_t oldflag = 0,dispflag = 0; CBlockIndex *pindex; arith_uint256 easydiff,bnTarget,hashval,sum,ave; bool fNegative,fOverflow; int32_t i,n,m,ht,percPoS,diff,val; *percPoSp = percPoS = 0; - if ( height <= 10 || (ASSETCHAINS_STAKED == 100 && height <= 100) ) + + if ( height <= 10 || (ASSETCHAINS_STAKED == 100 && height <= 100) ) return(target); + sum = arith_uint256(0); ave = sum; - easydiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); + easydiff.SetCompact(STAKING_MIN_DIFF,&fNegative,&fOverflow); for (i=n=m=0; i<100; i++) { ht = height - 100 + i; @@ -1387,12 +1375,22 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he sum += UintToArith256(pindex->GetBlockHash()); m++; } - } + } //else fprintf(stderr, "pindex returned null ht.%i\n",ht); if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 && (i % 10) == 9 ) fprintf(stderr," %d, ",percPoS); } if ( m+n < 100 ) - percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100; + { + // We do actual PoS % at the start. Requires coin distribution in first 10 blocks! + if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) + percPoS = (percPoS*100) / (m+n); + else + // This seems to be inverse. The actual PoS % is backwards in the first 100 blocks. + // I dont't understand the math here, or why its backwards, so I am just disabling it for VerusHash. + // No doubt this is probably wrong for equihash aswell, we may need to test an equihash chain with the rule above. + // Need to ask james what the deal is here! Seems to be causeing ALL the problems. + percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100; + } if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) fprintf(stderr," -> %d%% percPoS vs goalperc.%d ht.%d\n",percPoS,goalperc,height); *percPoSp = percPoS; @@ -1451,13 +1449,127 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he fprintf(stderr," ht.%d percPoS.%d vs goal.%d -> diff %d\n",height,percPoS,goalperc,goalperc - percPoS); } } - else bnTarget = ave; // recent ave is perfect + else + bnTarget = ave; // recent ave is perfect return(bnTarget); } +uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 txid,int32_t vout,uint32_t blocktime,uint32_t prevtime,char *destaddr,int32_t PoSperc) +{ + bool fNegative,fOverflow; uint8_t hashbuf[256]; char address[64]; bits256 addrhash; arith_uint256 hashval,mindiff,ratio,coinage256; uint256 hash,pasthash; int32_t segid,minage,i,iter=0; int64_t diff=0; uint32_t txtime,segid32,winner = 0 ; uint64_t value,coinage; + txtime = komodo_txtime2(&value,txid,vout,address); + if ( validateflag == 0 ) + { + //fprintf(stderr,"blocktime.%u -> ",blocktime); + if ( blocktime < prevtime+3 ) + blocktime = prevtime+3; + if ( blocktime < GetAdjustedTime()-60 ) + blocktime = GetAdjustedTime()+30; + //fprintf(stderr,"blocktime.%u txtime.%u\n",blocktime,txtime); + } + if ( value == 0 || txtime == 0 || blocktime == 0 || prevtime == 0 ) + { + //fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime); + return(0); + } + if ( value < SATOSHIDEN ) + return(0); + value /= SATOSHIDEN; + mindiff.SetCompact(STAKING_MIN_DIFF,&fNegative,&fOverflow); + ratio = (mindiff / bnTarget); + if ( (minage= nHeight*3) > 6000 ) // about 100 blocks + minage = 6000; + komodo_segids(hashbuf,nHeight-101,100); + segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout); + segid = ((nHeight + segid32) & 0x3f); + for (iter=0; iter<600; iter++) + { + if ( blocktime+iter+segid*2 < txtime+minage ) + continue; + diff = (iter + blocktime - txtime - minage); + if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) + { + /*if ( PoSperc < ASSETCHAINS_STAKED ) + { + // Under PoS % target and we need to increase diff. + //fprintf(stderr, "PoS too low diff.%i changed to.",diff); + diff = diff * ( (ASSETCHAINS_STAKED - PoSperc + 1) * (ASSETCHAINS_STAKED - PoSperc + 1) * ( nHeight < 50 ? 1000 : 1)); + //fprintf(stderr, "%i \n",diff); + } + else */ + if ( PoSperc > ASSETCHAINS_STAKED ) + { + // Over PoS target need to lower diff. + //fprintf(stderr, "PoS too high diff.%i changed to.",diff); + diff = diff / ( (PoSperc - ASSETCHAINS_STAKED + 1) * (PoSperc - ASSETCHAINS_STAKED + 1) ); + //fprintf(stderr, "%i \n",diff); + } + } + if ( diff < 0 ) + diff = 60; + else if ( diff > 3600*24*30 ) + { + //printf("diff.%d (iter.%d blocktime.%u txtime.%u minage.%d)\n",(int32_t)diff,iter,blocktime,txtime,(int32_t)minage); + diff = 3600*24*30; + } + if ( iter > 0 ) + diff += segid*2; + coinage = (value * diff); + if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) + { + if ( PoSperc < ASSETCHAINS_STAKED ) + { + // Under PoS % target and we need to increase diff. + //fprintf(stderr, "PoS too low diff.%i changed to.",diff); + if ( blocktime+iter+segid*2 > prevtime+128 ) + coinage *= ((blocktime+iter+segid*2) - (prevtime+102)); + //fprintf(stderr, "%i \n",diff); + } + } + if ( blocktime+iter+segid*2 > prevtime+480 ) + coinage *= ((blocktime+iter+segid*2) - (prevtime+400)); + coinage256 = arith_uint256(coinage+1); + hashval = ratio * (UintToArith256(hash) / coinage256); + if ( hashval <= bnTarget ) + { + winner = 1; + if ( validateflag == 0 ) + { + //fprintf(stderr,"winner blocktime.%u iter.%d segid.%d\n",blocktime,iter,segid); + blocktime += iter; + blocktime += segid * 2; + } + break; + } + if ( validateflag != 0 ) + { + /*for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); + fprintf(stderr," vs "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff); */ + break; + } + } + //fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner); + if ( 0 && validateflag != 0 ) + { + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); + fprintf(stderr," vs "); + for (i=31; i>=24; i--) + fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); + fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d ht.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff,nHeight); + } + if ( nHeight < 10 ) + return(blocktime); + return(blocktime * winner); +} + int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_uint256 bnTarget,arith_uint256 bhash) { - CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress; + CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress; arith_uint256 POWTarget; if ( ASSETCHAINS_STAKED == 100 && height <= 10 ) return(1); BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash()); @@ -1469,6 +1581,9 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ return(0); else return(1); } + // Get PoSperc and POW Target. slowflag only here, calling it when blocks out of order causes problems. + if ( slowflag != 0 ) + POWTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); txn_count = pblock->vtx.size(); //fprintf(stderr,"checkblock n.%d vins.%d vouts.%d %.8f %.8f\n",txn_count,(int32_t)pblock->vtx[txn_count-1].vin.size(),(int32_t)pblock->vtx[txn_count-1].vout.size(),(double)pblock->vtx[txn_count-1].vout[0].nValue/COIN,(double)pblock->vtx[txn_count-1].vout[1].nValue/COIN); if ( txn_count > 1 && pblock->vtx[txn_count-1].vin.size() == 1 && pblock->vtx[txn_count-1].vout.size() == 1 + (ASSETCHAINS_MARMARA!=0) ) @@ -1479,17 +1594,17 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ txid = pblock->vtx[txn_count-1].vin[0].prevout.hash; vout = pblock->vtx[txn_count-1].vin[0].prevout.n; - if ( prevtime != 0 ) + if ( slowflag != 0 && prevtime != 0 ) { if ( komodo_isPoS(pblock,height) != 0 ) { - eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+27,(char *)""); + eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+27,(char *)"",PoSperc); } if ( eligible == 0 || eligible > pblock->nTime ) { if ( 0 && ASSETCHAINS_STAKED < 100 ) fprintf(stderr,"komodo_is_PoSblock PoS failure ht.%d eligible.%u vs blocktime.%u, lag.%d -> check to see if it is PoW block\n",height,eligible,(uint32_t)pblock->nTime,(int32_t)(eligible - pblock->nTime)); - if ( slowflag != 0 && pindex != 0 ) + if ( pindex != 0 ) { pindex->segid = -1; //fprintf(stderr,"PoW block detected set segid.%d <- %d\n",height,pindex->segid); @@ -1498,24 +1613,20 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ else { isPoS = 2; // 2 means staking utxo validated - if ( slowflag != 0 && height > 100 ) + CTxDestination voutaddress; char voutaddr[64]; + if ( ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) ) { - CTxDestination voutaddress; char voutaddr[64]; - if ( ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) ) - { - strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); - segid = komodo_segid32(voutaddr) & 0x3f; - //fprintf(stderr,"komodo_segid.(%d) -> %d\n",height,segid); - } - if ( pindex != 0 && segid >= 0 ) - { - pindex->segid = segid; - //fprintf(stderr,"B set segid.%d <- %d\n",height,pindex->segid); - } //else fprintf(stderr,"unexpected null pindex for slowflag set ht.%d segid.%d:%d\n",height,pindex!=0?pindex->segid:-3,segid); + strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str()); + segid = komodo_segid32(voutaddr) & 0x3f; } + if ( pindex != 0 && segid >= 0 ) + { + pindex->segid = segid; + //fprintf(stderr,"PoS block set segid.%d <- %d\n",height,pindex->segid); + } //else fprintf(stderr,"unexpected null pindex for slowflag set ht.%d segid.%d:%d\n",height,pindex!=0?pindex->segid:-3,segid); } - } - if ( slowflag == 0 && isPoS == 0 ) // maybe previous block is not seen yet, do the best approx + } + else if ( slowflag == 0 ) // previous blocks are not seen yet, do the best approx { if ( komodo_isPoS(pblock,height) != 0 ) isPoS = 1; @@ -1527,10 +1638,9 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ fprintf(stderr,"ht.%d isPoS.%d utxo not validated -> must be PoW fake\n",height,isPoS); isPoS = 0; } - else + else if ( ASSETCHAINS_STAKED != 100 ) { - bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); - if ( bhash < bnTarget ) + if ( bhash < POWTarget ) { //fprintf(stderr,"ht.%d isPoS but meets PoW diff!\n",height); isPoS = 0; @@ -1722,10 +1832,213 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) return(isPOS); } +uint64_t komodo_notarypayamount(int32_t nHeight, int64_t notarycount) +{ + int8_t curEra = 0; int64_t ret = 0; + // if we have an end block in the first era, find our current era + if ( ASSETCHAINS_ENDSUBSIDY[0] > 1 ) + { + for ( curEra = 0; curEra <= ASSETCHAINS_LASTERA; curEra++ ) + { + if ( ASSETCHAINS_ENDSUBSIDY[curEra] > nHeight || ASSETCHAINS_ENDSUBSIDY[curEra] == 0 ) + break; + } + } + if ( curEra > ASSETCHAINS_LASTERA ) + return(0); + + if ( notarycount == 0 ) + { + fprintf(stderr, "komodo_notarypayamount failed num notaries is 0!\n"); + return(0); + } + // Because of reorgs we cannot use the notarized height value. + // We need to basically guess here and just pay some static amount. + // Has the unwanted effect of varying coin emission, but cannot be helped. + fprintf(stderr, "era.%i paying total of %lu\n",curEra, ASSETCHAINS_NOTARY_PAY[curEra]); + ret = ASSETCHAINS_NOTARY_PAY[curEra] / notarycount; + return(ret); +} + +int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len) +{ + // Check the notarisation is valid, and extract notarised height. + uint64_t voutmask; + uint8_t scriptbuf[10001]; + int32_t isratification,specialtx,notarizedheight; + + if ( len >= sizeof(uint32_t) && len <= sizeof(scriptbuf) ) + { + memcpy(scriptbuf,script,len); + if ( komodo_voutupdate(true,&isratification,0,scriptbuf,len,height,uint256(),1,1,&voutmask,&specialtx,¬arizedheight,0,1,0,timestamp) == -2 ) + { + fprintf(stderr, ">>>>>>VALID NOTARIZATION ht.%i\n",notarizedheight); + } + else + { + // This should no longer happen. Unless notaries are making actual invalid notarizations. + fprintf(stderr, "<<<<< &NotarisationNotaries, uint32_t timestamp, int32_t height, uint8_t *script, int32_t len) +{ + // fetch notary pubkey array. + uint64_t total = 0, AmountToPay = 0; + int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0}; + numSN = komodo_notaries(notarypubkeys, height, timestamp); + + // No point going further, no notaries can be paid. + if ( notarypubkeys[0][0] == 0 ) + return(0); + + // Check the notarisation is valid. + int32_t notarizedheight = komodo_getnotarizedheight(timestamp, height, script, len); + if ( notarizedheight == 0 ) + return(0); + + // resize coinbase vouts to number of notary nodes +1 for coinbase itself. + txNew.vout.resize(NotarisationNotaries.size()+1); + + // Calcualte the amount to pay according to the current era. + AmountToPay = komodo_notarypayamount(height,NotarisationNotaries.size()); + if ( AmountToPay == 0 ) + return(0); + + // loop over notarisation vins and add transaction to coinbase. + // Commented prints here can be used to verify manually the pubkeys match. + for (int8_t n = 0; n < NotarisationNotaries.size(); n++) + { + uint8_t *ptr; + txNew.vout[n+1].scriptPubKey.resize(35); + ptr = (uint8_t *)&txNew.vout[n+1].scriptPubKey[0]; + ptr[0] = 33; + for (int8_t i=0; i<33; i++) + { + ptr[i+1] = notarypubkeys[NotarisationNotaries[n]][i]; + //fprintf(stderr,"%02x",ptr[i+1]); + } + ptr[34] = OP_CHECKSIG; + //fprintf(stderr," set notary %i PUBKEY33 into vout[%i] amount.%lu\n",NotarisationNotaries[n],n+1,AmountToPay); + txNew.vout[n+1].nValue = AmountToPay; + total += txNew.vout[n+1].nValue; + } + return(total); +} + +bool GetNotarisationNotaries(uint8_t notarypubkeys[64][33], int8_t &numNN, const std::vector &vin, std::vector &NotarisationNotaries) +{ + uint8_t *script; int32_t scriptlen; + if ( notarypubkeys[0][0] == 0 ) + return false; + BOOST_FOREACH(const CTxIn& txin, vin) + { + uint256 hash; CTransaction tx1; + if ( GetTransaction(txin.prevout.hash,tx1,hash,false) ) + { + for (int8_t i = 0; i < numNN; i++) + { + script = (uint8_t *)&tx1.vout[txin.prevout.n].scriptPubKey[0]; + scriptlen = (int32_t)tx1.vout[txin.prevout.n].scriptPubKey.size(); + if ( scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script+1,notarypubkeys[i],33) == 0 ) + NotarisationNotaries.push_back(i); + } + } else return false; + } + return true; +} + +uint64_t komodo_checknotarypay(CBlock *pblock,int32_t height) +{ + std::vector NotarisationNotaries; uint8_t *script; int32_t scriptlen; + uint64_t timestamp = pblock->nTime; + int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0}; + numSN = komodo_notaries(notarypubkeys, height, timestamp); + if ( !GetNotarisationNotaries(notarypubkeys, numSN, pblock->vtx[1].vin, NotarisationNotaries) ) + return(0); + + // check a notary didnt sign twice (this would be an invalid notarisation later on and cause problems) + std::set checkdupes( NotarisationNotaries.begin(), NotarisationNotaries.end() ); + if ( checkdupes.size() != NotarisationNotaries.size() ) { + fprintf(stderr, "Possible notarisation is signed multiple times by same notary. It is invalid.\n"); + return(0); + } + const CChainParams& chainparams = Params(); + const Consensus::Params &consensusParams = chainparams.GetConsensus(); + uint64_t totalsats = 0; + CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, height); + if ( pblock->vtx[1].vout.size() == 2 && pblock->vtx[1].vout[1].nValue == 0 ) + { + // Get the OP_RETURN for the notarisation + uint8_t *script = (uint8_t *)&pblock->vtx[1].vout[1].scriptPubKey[0]; + int32_t scriptlen = (int32_t)pblock->vtx[1].vout[1].scriptPubKey.size(); + if ( script[0] == OP_RETURN ) + { + // Create the coinbase tx again, using the extracted data, this is the same function the miner uses, with the same data. + // This allows us to know exactly that the coinbase is correct. + totalsats = komodo_notarypay(txNew, NotarisationNotaries, pblock->nTime, height, script, scriptlen); + } + else + { + fprintf(stderr, "vout 2 of notarisation is not OP_RETURN scriptlen.%i\n", scriptlen); + return(0); + } + } else return(0); + + // if notarypay fails, because the notarisation is not valid, exit now as txNew was not created. + // This should never happen, as the notarisation is checked before this function is called. + if ( totalsats == 0 ) + { + fprintf(stderr, "notary pay returned 0!\n"); + return(0); + } + + int8_t n = 0, i = 0, matches = 0; + uint64_t total = 0, AmountToPay = 0; + + // get the pay amount from the created tx. + AmountToPay = txNew.vout[1].nValue; + + // Check the created coinbase pays the correct notaries. + BOOST_FOREACH(const CTxOut& txout, pblock->vtx[0].vout) + { + // skip the coinbase paid to the miner. + if ( n == 0 ) + { + n++; + continue; + } + // Check the pubkeys match the pubkeys in the notarisation. + script = (uint8_t *)&txout.scriptPubKey[0]; + scriptlen = (int32_t)txout.scriptPubKey.size(); + if ( scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script+1,notarypubkeys[NotarisationNotaries[n-1]],33) == 0 ) + { + // check the value is correct + if ( pblock->vtx[0].vout[n].nValue == AmountToPay ) + { + matches++; + total += txout.nValue; + //fprintf(stderr, "MATCHED AmountPaid.%lu notaryid.%i\n",AmountToPay,NotarisationNotaries[n-1]); + } + else fprintf(stderr, "NOT MATCHED AmountPaid.%lu AmountToPay.%lu notaryid.%i\n", pblock->vtx[0].vout[n].nValue, AmountToPay, NotarisationNotaries[n-1]); + } + n++; + } + if ( matches != 0 && matches == NotarisationNotaries.size() && totalsats == total ) + { + fprintf(stderr, "Validated coinbase matches notarisation in tx position 1.\n" ); + return(totalsats); + } + return(0); +} + int64_t komodo_checkcommission(CBlock *pblock,int32_t height) { int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0; - if ( ASSETCHAINS_COMMISSION != 0 ) + if ( ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 ) { checktoshis = komodo_commission(pblock,height); if ( checktoshis >= 10000 && pblock->vtx[0].vout.size() < 2 ) @@ -1737,10 +2050,13 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height) { script = (uint8_t *)&pblock->vtx[0].vout[1].scriptPubKey[0]; scriptlen = (int32_t)pblock->vtx[0].vout[1].scriptPubKey.size(); - //int32_t i; - //for (i=0; ivtx[0].vout[1].nValue/COIN); + if ( 0 ) + { + int32_t i; + for (i=0; ivtx[0].vout[1].nValue/COIN); + } if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { if ( ASSETCHAINS_SCRIPTPUB.size()/2 == scriptlen && scriptlen < sizeof(scripthex) ) @@ -1756,7 +2072,13 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height) matched = 25; if ( matched == 0 ) { - fprintf(stderr," payment to wrong pubkey scriptlen.%d, scriptpub[%d] checktoshis.%llu\n",scriptlen,(int32_t)ASSETCHAINS_SCRIPTPUB.size()/2,(long long)checktoshis); + if ( 0 && ASSETCHAINS_SCRIPTPUB.size() > 1 ) + { + int32_t i; + for (i=0; i bnTarget ) + bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED); + if ( bhash > bnTarget && height > 100 ) { for (i=31; i>=16; i--) fprintf(stderr,"%02x",((uint8_t *)&bhash)[i]); @@ -1852,7 +2173,18 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); fprintf(stderr," ht.%d PoW diff violation PoSperc.%d vs goalperc.%d\n",height,PoSperc,(int32_t)ASSETCHAINS_STAKED); return(-1); - } else failed = 0; + } + else + { + failed = 0; + CBlockIndex *pindex; + BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash()); + pindex = it != mapBlockIndex.end() ? it->second : NULL; + if ( pindex != 0 && pindex->segid == -2 ) { + pindex->segid = -1; + //fprintf(stderr,"PoW block detected set segid.%d <- %d\n",height,pindex->segid); + } + } } } else if ( is_PoSblock < 0 ) @@ -1893,6 +2225,32 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) return(-1); } } + // Consensus rule to force miners to mine the notary coinbase payment happens in ConnectBlock + // the default daemon miner, checks the actual vins so the only way this will fail, is if someone changes the miner, + // and then creates txs to the crypto address meeting min sigs and puts it in tx position 1. + // If they go through this effort, the block will still fail at connect block, and will be auto purged by the temp file fix. + if ( failed == 0 && ASSETCHAINS_NOTARY_PAY[0] != 0 && pblock->vtx[0].vout.size() > 1 ) + { + // We check the full validation in ConnectBlock directly to get the amount for coinbase. So just approx here. + if ( slowflag == 0 ) + { + // Check the notarisation tx is to the crypto address. + if ( !komodo_is_notarytx(pblock->vtx[1]) == 1 ) + { + fprintf(stderr, "notarisation is not to crypto address ht.%i\n",height); + return(-1); + } + // Check min sigs. + int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0}; + numSN = komodo_notaries(notarypubkeys, height, pblock->nTime); + if ( pblock->vtx[1].vin.size() < numSN/5 ) + { + fprintf(stderr, "ht.%i does not meet minsigs.%i sigs.%li\n",height,numSN/5,pblock->vtx[1].vin.size()); + return(-1); + } + } + } + //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); @@ -2036,70 +2394,15 @@ struct komodo_staking *komodo_addutxo(struct komodo_staking *array,int32_t *numk return(array); } -arith_uint256 _komodo_eligible(struct komodo_staking *kp,arith_uint256 ratio,uint32_t blocktime,int32_t iter,int32_t minage,int32_t segid,int32_t nHeight,uint32_t prevtime) -{ - int32_t diff; uint64_t coinage; arith_uint256 coinage256,hashval; - diff = (iter + blocktime - kp->txtime - minage); - if ( diff < 0 ) - diff = 60; - else if ( diff > 3600*24*30 ) - diff = 3600*24*30; - if ( iter > 0 ) - diff += segid*2; - coinage = ((uint64_t)kp->nValue * diff); - if ( blocktime+iter+segid*2 > prevtime+480 ) - coinage *= ((blocktime+iter+segid*2) - (prevtime+400)); - coinage256 = arith_uint256(coinage+1); - hashval = ratio * (kp->hashval / coinage256); - return(hashval); -} - -uint32_t komodo_eligible(arith_uint256 bnTarget,arith_uint256 ratio,struct komodo_staking *kp,int32_t nHeight,uint32_t blocktime,uint32_t prevtime,int32_t minage,uint8_t *hashbuf) -{ - int32_t maxiters = 600; uint256 hash; - int32_t segid,iter,diff; uint64_t coinage; arith_uint256 hashval,coinage256; - komodo_stakehash(&hash,kp->address,hashbuf,kp->txid,kp->vout); - kp->hashval = UintToArith256(hash); - segid = ((nHeight + kp->segid32) & 0x3f); - hashval = _komodo_eligible(kp,ratio,blocktime,maxiters,minage,segid,nHeight,prevtime); - /*for (int i=31; i>=16; i--) - fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); - fprintf(stderr," vs "); - for (int i=31; i>=16; i--) - fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]); - fprintf(stderr," b.%u minage.%d segid.%d ht.%d prev.%u\n",blocktime,minage,segid,nHeight,prevtime);*/ - if ( hashval <= bnTarget ) - { - for (iter=0; itertxtime+minage ) - continue; - hashval = _komodo_eligible(kp,ratio,blocktime,iter,minage,segid,nHeight,prevtime); - if ( hashval <= bnTarget ) - { - //fprintf(stderr,"winner %.8f blocktime.%u iter.%d segid.%d\n",(double)kp->nValue/COIN,blocktime,iter,segid); - blocktime += iter; - blocktime += segid * 2; - return(blocktime); - } - } - } else fprintf(stderr,"maxiters is not good enough\n"); - return(0); -} - -int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); -uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); - int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig) { static struct komodo_staking *array; static int32_t numkp,maxkp; static uint32_t lasttime; - set setAddress; struct komodo_staking *kp; int32_t winners,segid,minage,nHeight,counter=0,i,m,siglen=0,nMinDepth = 1,nMaxDepth = 99999999; vector vecOutputs; uint32_t block_from_future_rejecttime,besttime,eligible,eligible2,earliest = 0; CScript best_scriptPubKey; arith_uint256 mindiff,ratio,bnTarget; CBlockIndex *tipindex,*pindex; CTxDestination address; bool fNegative,fOverflow; uint8_t hashbuf[256]; CTransaction tx; uint256 hashBlock; + int32_t PoSperc; + set setAddress; struct komodo_staking *kp; int32_t winners,segid,minage,nHeight,counter=0,i,m,siglen=0,nMinDepth = 1,nMaxDepth = 99999999; vector vecOutputs; uint32_t block_from_future_rejecttime,besttime,eligible,earliest = 0; CScript best_scriptPubKey; arith_uint256 mindiff,ratio,bnTarget,tmpTarget; CBlockIndex *tipindex,*pindex; CTxDestination address; bool fNegative,fOverflow; uint8_t hashbuf[256]; CTransaction tx; uint256 hashBlock; if (!EnsureWalletIsAvailable(0)) return 0; - + bnTarget.SetCompact(nBits, &fNegative, &fOverflow); - mindiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); - ratio = (mindiff / bnTarget); assert(pwalletMain != NULL); *utxovaluep = 0; memset(utxotxidp,0,sizeof(*utxotxidp)); @@ -2108,10 +2411,12 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt if ( (tipindex= chainActive.Tip()) == 0 ) return(0); nHeight = tipindex->GetHeight() + 1; + // Get the PoS% so we can pass it to komodo_stake, this is to adjust PoS dofficulty when it is under the target %! + tmpTarget = komodo_PoWtarget(&PoSperc,bnTarget,nHeight,ASSETCHAINS_STAKED); 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); @@ -2129,7 +2434,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt } } - if ( time(NULL) > lasttime+600 || array == 0 || resetstaker ) + if ( resetstaker || array == 0 || time(NULL) > lasttime+600 ) { LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); @@ -2152,7 +2457,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; @@ -2179,14 +2484,14 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt mypk = pubkey2pk(Mypubkey()); Marmarapk = GetUnspendable(cp,0); GetCCaddress1of2(cp,coinaddr,Marmarapk,mypk); - SetCCunspents(unspentOutputs,coinaddr); + SetCCunspents(unspentOutputs,coinaddr,true); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; if ( (nValue= it->second.satoshis) < COIN ) continue; - if ( GetTransaction(txid,tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(txid,vout) == 0 ) + if ( GetTransaction(txid,tx,hashBlock,true) != 0 && (pindex= komodo_getblockindex(hashBlock)) != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { const CScript &scriptPubKey = tx.vout[vout].scriptPubKey; if ( DecodeMaramaraCoinbaseOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,pk,ht,unlockht) != 0 && pk == mypk ) @@ -2201,27 +2506,25 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt //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; + block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + 57; for (i=winners=0; iGetHeight()+1 > nHeight ) { fprintf(stderr,"chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter); return(0); } kp = &array[i]; - if ( (eligible2= komodo_eligible(bnTarget,ratio,kp,nHeight,*blocktimep,(uint32_t)tipindex->nTime+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); + eligible = komodo_stake(0,bnTarget,nHeight,kp->txid,kp->vout,0,(uint32_t)tipindex->nTime+27,kp->address,PoSperc); + //fprintf(stderr,"i.%d %u vs %u\n",i,eligible2,eligible); if ( eligible > 0 ) { besttime = m = 0; - if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address) ) + if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address,PoSperc) ) { - while ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address) ) + while ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address,PoSperc) ) { besttime = eligible; eligible--; @@ -2248,7 +2551,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt decode_hex((uint8_t *)utxotxidp,32,(char *)kp->txid.GetHex().c_str()); *utxovoutp = kp->vout; *txtimep = kp->txtime;//(uint32_t)out.tx->nLockTime; - 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); + //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"); } diff --git a/src/komodo_cJSON.c b/src/komodo_cJSON.c old mode 100755 new mode 100644 diff --git a/src/komodo_cJSON.h b/src/komodo_cJSON.h old mode 100755 new mode 100644 diff --git a/src/komodo_defs.h b/src/komodo_defs.h index 0ea97d6db..4db390494 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -34,45 +34,78 @@ #define GETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] & (1 << ((bitoffset) & 7))) #define CLEARBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] &= ~(1 << ((bitoffset) & 7))) +#define KOMODO_MAXNVALUE (((uint64_t)1 << 63) - 1) +#define KOMODO_BIT63SET(x) ((x) & ((uint64_t)1 << 63)) +#define KOMODO_VALUETOOBIG(x) ((x) > (uint64_t)10000000001*COIN) +#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) + extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; extern uint32_t ASSETCHAIN_INIT, ASSETCHAINS_MAGIC; -extern int32_t VERUS_BLOCK_POSUNITS, ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER; -extern uint64_t ASSETCHAINS_SUPPLY; +extern int32_t VERUS_BLOCK_POSUNITS, ASSETCHAINS_LWMAPOS, ASSETCHAINS_SAPLING, ASSETCHAINS_OVERWINTER,ASSETCHAINS_BLOCKTIME; +extern uint64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_FOUNDERS_REWARD; extern uint64_t ASSETCHAINS_TIMELOCKGTE; extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH,ASSETCHAINS_EQUIHASH,KOMODO_INITDONE; -extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,KOMODO_ON_DEMAND,KOMODO_PASSPORT_INITDONE; -extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_STAKED; +extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,KOMODO_ON_DEMAND,KOMODO_PASSPORT_INITDONE,ASSETCHAINS_STAKED; +extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_LASTERA,ASSETCHAINS_CBOPRET; extern bool VERUS_MINTBLOCKS; -extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[]; +extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[]; extern const char *ASSETCHAINS_ALGORITHMS[]; extern int32_t VERUS_MIN_STAKEAGE; -extern uint32_t ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern uint32_t ASSETCHAINS_VERUSHASH, ASSETCHAINS_VERUSHASHV1_1, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[]; extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY,ASSETCHAINS_SCRIPTPUB; extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_MARMARA; +extern std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; -extern char ASSETCHAINS_SYMBOL[65]; extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE; extern uint32_t ASSETCHAINS_CC; -extern char ASSETCHAINS_SYMBOL[]; extern std::string CCerror,ASSETCHAINS_CCLIB; extern uint8_t ASSETCHAINS_CCDISABLES[256]; extern int32_t USE_EXTERNAL_PUBKEY; extern std::string NOTARY_PUBKEY; extern int32_t KOMODO_EXCHANGEWALLET; -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern int32_t VERUS_MIN_STAKEAGE; extern std::string DONATION_PUBKEY; extern uint8_t ASSETCHAINS_PRIVATE; extern int32_t USE_EXTERNAL_PUBKEY; +extern char NOTARYADDRS[64][64]; +int tx_height( const uint256 &hash ); +extern std::vector vWhiteListAddress; +void komodo_netevent(std::vector payload); + +#define IGUANA_MAXSCRIPTSIZE 10001 +#define KOMODO_KVDURATION 1440 +#define KOMODO_KVBINARY 2 +#define PRICES_SMOOTHWIDTH 1 +uint64_t komodo_paxprice(uint64_t *seedp,int32_t height,char *base,char *rel,uint64_t basevolume); +int32_t komodo_paxprices(int32_t *heights,uint64_t *prices,int32_t max,char *base,char *rel); +int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); +char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len); +int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width); +int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); + +int32_t komodo_longestchain(); +int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); +int8_t komodo_segid(int32_t nocache,int32_t height); +int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); +char *komodo_pricename(char *name,int32_t ind); +int32_t komodo_priceind(char *symbol); +int32_t komodo_pricesinit(); +int64_t komodo_priceave(int64_t *tmpbuf,int64_t *correlated,int32_t cskip); +int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t rawskip,uint32_t *nonzprices,int32_t smoothwidth); +int32_t komodo_nextheight(); +uint32_t komodo_heightstamp(int32_t height); +int64_t komodo_pricemult(int32_t ind); +int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); +uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); + #endif diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 02ab67407..a6b5b4566 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -16,6 +16,17 @@ // paxdeposit equivalent in reverse makes opreturn and KMD does the same in reverse #include "komodo_defs.h" +/*#include "secp256k1/include/secp256k1.h" +#include "secp256k1/include/secp256k1_schnorrsig.h" +#include "secp256k1/include/secp256k1_musig.h" + +int32_t dummy_linker_tricker() +{ + secp256k1_context *ctx = 0; std::vector musig64; CPubKey pk; secp256k1_schnorrsig musig; secp256k1_pubkey combined_pk; + if ( secp256k1_schnorrsig_parse((const secp256k1_context *)ctx,&musig,(const uint8_t *)&musig64[0]) > 0 && secp256k1_ec_pubkey_parse(ctx,&combined_pk,pk.begin(),33) > 0 ) + return(1); +}*/ + int32_t MarmaraValidateCoinbase(int32_t height,CTransaction tx); int32_t pax_fiatstatus(uint64_t *available,uint64_t *deposited,uint64_t *issued,uint64_t *withdrawn,uint64_t *approved,uint64_t *redeemed,char *base) @@ -642,7 +653,7 @@ int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max) if ( sizeof(banned_txids)/sizeof(*banned_txids) > max ) { fprintf(stderr,"komodo_bannedset: buffer too small %ld vs %d\n",sizeof(banned_txids)/sizeof(*banned_txids),max); - exit(-1); + StartShutdown(); } for (i=0; i 1) || + ((ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD) && height > 1) || NetworkUpgradeActive(height, Params().GetConsensus(), Consensus::UPGRADE_SAPLING) ) { n = block.vtx[0].vout.size(); @@ -761,7 +772,7 @@ int32_t komodo_check_deposit(int32_t height,const CBlock& block,uint32_t prevtim else { checktoshis = 0; - if ( ASSETCHAINS_COMMISSION != 0 && height > 1 ) + if ( (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD) && height > 1 ) { if ( (checktoshis= komodo_checkcommission((CBlock *)&block,height)) < 0 ) { @@ -1536,3 +1547,1259 @@ void komodo_passport_iteration() printf("READY for %s RPC calls at %u! done PASSPORT %s refid.%d\n",ASSETCHAINS_SYMBOL,(uint32_t)time(NULL),ASSETCHAINS_SYMBOL,refid); } } + + +extern std::vector Mineropret; // opreturn data set by the data gathering code +#define PRICES_ERRORRATE (COIN / 100) // maximum acceptable change, set at 1% +#define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR +#define KOMODO_LOCALPRICE_CACHESIZE 13 +#define KOMODO_MAXPRICES 2048 +#define PRICES_SMOOTHWIDTH 1 +#define PRICES_MAXDATAPOINTS 8 + +#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) + +const char *Cryptos[] = { "KMD", "ETH" }; // must be on binance (for now) +// "LTC", "BCHABC", "XMR", "IOTA", "ZEC", "WAVES", "LSK", "DCR", "RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" +const char *Forex[] = +{ "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" +}; // must be in ECB list + +struct komodo_extremeprice +{ + uint256 blockhash; + uint32_t pricebits,timestamp; + int32_t height; + int16_t dir,ind; +} ExtremePrice; + +struct komodo_priceinfo +{ + FILE *fp; + char symbol[64]; +} PRICES[KOMODO_MAXPRICES]; + +uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][KOMODO_MAXPRICES];//4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; +int64_t PriceMult[KOMODO_MAXPRICES]; +int32_t komodo_cbopretsize(uint64_t flags); + +void komodo_PriceCache_shift() +{ + int32_t i; + for (i=KOMODO_LOCALPRICE_CACHESIZE-1; i>0; i--) + memcpy(PriceCache[i],PriceCache[i-1],sizeof(PriceCache[i])); + memcpy(PriceCache[0],Mineropret.data(),Mineropret.size()); +} + +int32_t _komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,CBlock *block) +{ + CTransaction tx; int32_t numvouts; std::vector vopret; + tx = block->vtx[0]; + numvouts = (int32_t)tx.vout.size(); + GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); + if ( vopret.size() >= PRICES_SIZEBIT0 ) + { + if ( seedp != 0 ) + memcpy(seedp,&block->hashMerkleRoot,sizeof(*seedp)); + memcpy(heightbits,vopret.data(),vopret.size()); + return((int32_t)(vopret.size()/sizeof(uint32_t))); + } + return(-1); +} + +// komodo_heightpricebits() extracts the price data in the coinbase for nHeight +int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight) +{ + CBlockIndex *pindex; CBlock block; + if ( seedp != 0 ) + *seedp = 0; + if ( (pindex= komodo_chainactive(nHeight)) != 0 ) + { + if ( komodo_blockload(block,pindex) == 0 ) + { + return(_komodo_heightpricebits(seedp,heightbits,&block)); + } + } + fprintf(stderr,"couldnt get pricebits for %d\n",nHeight); + return(-1); +} + +/* + komodo_pricenew() is passed in a reference price, the change tolerance and the proposed price. it needs to return a clipped price if it is too big and also set a flag if it is at or above the limit + */ +uint32_t komodo_pricenew(char *maxflagp,uint32_t price,uint32_t refprice,int64_t tolerance) +{ + uint64_t highprice,lowprice; + if ( refprice < 2 ) + return(0); + highprice = ((uint64_t)refprice * (COIN + tolerance)) / COIN; // calc highest acceptable price + lowprice = ((uint64_t)refprice * (COIN - tolerance)) / COIN; // and lowest + if ( highprice == refprice ) + highprice++; + if ( lowprice == refprice ) + lowprice--; + if ( price >= highprice ) + { + //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + if ( price > highprice ) // return non-zero only if we violate the tolerance + { + *maxflagp = 2; + return(highprice); + } + *maxflagp = 1; + } + else if ( price <= lowprice ) + { + //fprintf(stderr,"low %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + if ( price < lowprice ) + { + *maxflagp = -2; + return(lowprice); + } + *maxflagp = -1; + } + return(0); +} + +// komodo_pricecmp() returns -1 if any of the prices are beyond the tolerance +int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *pricebitsA,uint32_t *pricebitsB,int64_t tolerance) +{ + int32_t i; uint32_t newprice; + for (i=1; i newprice.%u out of tolerance maxflag.%d\n",nHeight,i,n,pricebitsB[i],pricebitsA[i],newprice,maxflags[i]); + return(-1); + } + } + return(0); +} + +// komodo_priceclamp() clamps any price that is beyond tolerance +int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int64_t tolerance) +{ + int32_t i; uint32_t newprice; char maxflags[KOMODO_MAXPRICES]; + memset(maxflags,0,sizeof(maxflags)); + for (i=1; i %u\n",i,n,refprices[i],pricebits[i],newprice); + pricebits[i] = newprice; + } + } + return(0); +} + +// komodo_mineropret() returns a valid pricedata to add to the coinbase opreturn for nHeight +CScript komodo_mineropret(int32_t nHeight) +{ + CScript opret; char maxflags[KOMODO_MAXPRICES]; uint32_t pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES]; int32_t maxflag,i,n,numzero=0; + if ( Mineropret.size() >= PRICES_SIZEBIT0 ) + { + n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); + numzero = 1; + while ( numzero > 0 ) + { + memcpy(pricebits,Mineropret.data(),Mineropret.size()); + for (i=numzero=0; i 0 ) + { + memcpy(pricebits,Mineropret.data(),Mineropret.size()); + memset(maxflags,0,sizeof(maxflags)); + if ( komodo_pricecmp(0,n,maxflags,pricebits,prevbits,PRICES_ERRORRATE) < 0 ) + { + // if the new prices are outside tolerance, update Mineropret with clamped prices + komodo_priceclamp(n,pricebits,prevbits,PRICES_ERRORRATE); + //fprintf(stderr,"update Mineropret to clamped prices\n"); + memcpy(Mineropret.data(),pricebits,Mineropret.size()); + } + } + int32_t i; + for (i=0; i vopret; char maxflags[KOMODO_MAXPRICES]; uint256 bhash; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now; + now = (uint32_t)time(NULL); + if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) + { + bhash = block->GetHash(); + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() >= PRICES_SIZEBIT0 ) + { + n = (int32_t)(vopret.size() / sizeof(uint32_t)); + memcpy(pricebits,vopret.data(),Mineropret.size()); + memset(maxflags,0,sizeof(maxflags)); + if ( nHeight > 2 ) + { + prevtime = previndex->nTime; + lag = (int32_t)(now - pricebits[0]); + lag2 = (int32_t)(pricebits[0] - prevtime); + lag3 = (int32_t)(block->nTime - pricebits[0]); + if ( lag < -60 ) // avoid data from future + { + fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + return(-1); + } + if ( lag2 < -60 ) //testchain_exemption ) // must be close to last block timestamp + { + fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); + if ( nHeight > testchain_exemption ) + return(-1); + } + if ( lag3 < -60 || lag3 > ASSETCHAINS_BLOCKTIME ) + { + fprintf(stderr,"C ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); + if ( nHeight > testchain_exemption ) + return(-1); + } + btcusd = (double)pricebits[1]/10000; + btcgbp = (double)pricebits[2]/10000; + btceur = (double)pricebits[3]/10000; + fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); + if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) + { + if ( nHeight < testchain_exemption ) + { + for (i=0; i= PRICES_SIZEBIT0 ) + { + memcpy(localbits,Mineropret.data(),Mineropret.size()); + if ( nHeight < testchain_exemption ) + { + for (i=0; i 0 && localbits[i] < prevbits[i] ) + { + if ( iter == 0 ) + break; + // second iteration checks recent prices to see if within local volatility + for (j=0; j= prevbits[i] ) + { + fprintf(stderr,"i.%d within recent localprices[%d] %u >= %u\n",i,j,PriceCache[j][i],prevbits[i]); + break; + } + if ( j == KOMODO_LOCALPRICE_CACHESIZE ) + { + komodo_queuelocalprice(1,nHeight,block->nTime,bhash,i,prevbits[i]); + break; + } + } + else if ( maxflag < 0 && localbits[i] > prevbits[i] ) + { + if ( iter == 0 ) + break; + for (j=0; jnTime,bhash,i,prevbits[i]); + break; + } + } + } + } + if ( i != n ) + { + if ( iter == 0 ) + { + fprintf(stderr,"force update prices\n"); + komodo_cbopretupdate(1); + memcpy(localbits,Mineropret.data(),Mineropret.size()); + } else return(-1); + } + } + } + } + if ( bhash == ExtremePrice.blockhash ) + { + fprintf(stderr,"approved a previously extreme price based on new data ht.%d vs %u vs %u\n",ExtremePrice.height,ExtremePrice.timestamp,(uint32_t)block->nTime); + memset(&ExtremePrice,0,sizeof(ExtremePrice)); + } + return(0); + } else fprintf(stderr,"wrong size %d vs %d, scriptPubKey size %d [%02x]\n",(int32_t)vopret.size(),(int32_t)Mineropret.size(),(int32_t)scriptPubKey.size(),scriptPubKey[0]); + return(-1); + } + return(0); +} + +char *nonportable_path(char *str) +{ + int32_t i; + for (i=0; str[i]!=0; i++) + if ( str[i] == '/' ) + str[i] = '\\'; + return(str); +} + +char *portable_path(char *str) +{ +#ifdef _WIN32 + return(nonportable_path(str)); +#else +#ifdef __PNACL + /*int32_t i,n; + if ( str[0] == '/' ) + return(str); + else + { + n = (int32_t)strlen(str); + for (i=n; i>0; i--) + str[i] = str[i-1]; + str[0] = '/'; + str[n+1] = 0; + }*/ +#endif + return(str); +#endif +} + +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) +{ + FILE *fp; + long filesize,buflen = *allocsizep; + uint8_t *buf = *bufp; + *lenp = 0; + if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) + { + fseek(fp,0,SEEK_END); + filesize = ftell(fp); + if ( filesize == 0 ) + { + fclose(fp); + *lenp = 0; + //printf("loadfile null size.(%s)\n",fname); + return(0); + } + if ( filesize > buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +void *filestr(long *allocsizep,char *_fname) +{ + long filesize = 0; char *fname,*buf = 0; void *retptr; + *allocsizep = 0; + fname = (char *)malloc(strlen(_fname)+1); + strcpy(fname,_fname); + retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); + free(fname); + return(retptr); +} + +cJSON *send_curl(char *url,char *fname) +{ + long fsize; char curlstr[1024],*jsonstr; cJSON *json=0; + sprintf(curlstr,"wget -q \"%s\" -O %s",url,fname); + if ( system(curlstr) == 0 ) + { + if ( (jsonstr= (char *)filestr((long *)&fsize,fname)) != 0 ) + { + json = cJSON_Parse(jsonstr); + free(jsonstr); + } + } + return(json); +} + +// get_urljson just returns the JSON returned by the URL using issue_curl + + +/* +const char *Techstocks[] = +{ "AAPL","ADBE","ADSK","AKAM","AMD","AMZN","ATVI","BB","CDW","CRM","CSCO","CYBR","DBX","EA","FB","GDDY","GOOG","GRMN","GSAT","HPQ","IBM","INFY","INTC","INTU","JNPR","MSFT","MSI","MU","MXL","NATI","NCR","NFLX","NTAP","NVDA","ORCL","PANW","PYPL","QCOM","RHT","S","SHOP","SNAP","SPOT","SYMC","SYNA","T","TRIP","TWTR","TXN","VMW","VOD","VRSN","VZ","WDC","XRX","YELP","YNDX","ZEN" +}; +const char *Metals[] = { "XAU", "XAG", "XPT", "XPD", }; + +const char *Markets[] = { "DJIA", "SPX", "NDX", "VIX" }; +*/ + +cJSON *get_urljson(char *url) +{ + char *jsonstr; cJSON *json = 0; + if ( (jsonstr= issue_curl(url)) != 0 ) + { + //fprintf(stderr,"(%s) -> (%s)\n",url,jsonstr); + json = cJSON_Parse(jsonstr); + free(jsonstr); + } + return(json); +} + +int32_t get_stockprices(uint32_t now,uint32_t *prices,std::vector symbols) +{ + char url[32768],*symbol,*timestr; cJSON *json,*obj; int32_t i,n=0,retval=-1; uint32_t uprice,timestamp; + sprintf(url,"https://api.iextrading.com/1.0/tops/last?symbols=%s",GetArg("-ac_stocks","").c_str()); + if ( (json= send_curl(url,(char *)"iex")) != 0 ) //if ( (json= get_urljson(url)) != 0 ) + { + if ( (n= cJSON_GetArraySize(json)) > 0 ) + { + retval = n; + for (i=0; i now+60 || timestamp < now-ASSETCHAINS_BLOCKTIME ) + { + fprintf(stderr,"time error.%d (%u vs %u)\n",timestamp-now,timestamp,now); + retval = -1; + }*/ + if ( symbols[i] != symbol ) + { + retval = -1; + fprintf(stderr,"MISMATCH."); + } + fprintf(stderr,"(%s %u) ",symbol,uprice); + } + } + fprintf(stderr,"numstocks.%d\n",n); + } + //https://api.iextrading.com/1.0/tops/last?symbols=AAPL -> [{"symbol":"AAPL","price":198.63,"size":100,"time":1555092606076}] + free_json(json); + } + return(retval); +} + +uint32_t get_dailyfx(uint32_t *prices) +{ + //{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"} + char url[512],*datestr; cJSON *json,*rates; int32_t i; uint32_t datenum=0,price = 0; + sprintf(url,"https://api.openrates.io/latest?base=USD"); + if ( (json= send_curl(url,(char *)"dailyfx")) != 0 ) + { + if ( (rates= jobj(json,(char *)"rates")) != 0 ) + { + for (i=0; i strvec) +{ + int32_t i,errs=0; uint32_t price; char *symbol; + for (i=0; i 333 ) // for debug only! +// ASSETCHAINS_CBOPRET = 7; + size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); + if ( Mineropret.size() < size ) + Mineropret.resize(size); + size = PRICES_SIZEBIT0; + if ( (forceflag != 0 || now > lastbtc+120) && get_btcusd(pricebits) == 0 ) + { + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(PriceCache[0],pricebits,PRICES_SIZEBIT0); + flags |= 1; + } + if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) + { + if ( now > lasttime+3600*5 || forexprices[0] == 0 ) // cant assume timestamp is valid for forex price as it is a daily weekday changing thing anyway. + { + get_dailyfx(forexprices); + if ( flags == 0 ) + komodo_PriceCache_shift(); + flags |= 2; + memcpy(&PriceCache[0][size/sizeof(uint32_t)],forexprices,sizeof(forexprices)); + } + size += (sizeof(Forex)/sizeof(*Forex)) * sizeof(uint32_t); + } + if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) + { + if ( forceflag != 0 || flags != 0 ) + { + get_cryptoprices(pricebuf,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos)),ASSETCHAINS_PRICES); + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],pricebuf,(sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t)); + flags |= 4; // very rarely we can see flags == 6 case + } + size += (sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t); + } + now = (uint32_t)time(NULL); + if ( (ASSETCHAINS_CBOPRET & 8) != 0 ) + { + if ( forceflag != 0 || flags != 0 ) + { + if ( get_stockprices(now,pricebuf,ASSETCHAINS_STOCKS) == ASSETCHAINS_STOCKS.size() ) + { + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],pricebuf,ASSETCHAINS_STOCKS.size() * sizeof(uint32_t)); + flags |= 8; // very rarely we can see flags == 10 case + } + } + size += (ASSETCHAINS_STOCKS.size()) * sizeof(uint32_t); + } + if ( flags != 0 ) + { + if ( (flags & 1) != 0 ) + lastbtc = now; + if ( (flags & 2) != 0 ) + lasttime = now; + memcpy(Mineropret.data(),PriceCache[0],size); + if ( ExtremePrice.dir != 0 && ExtremePrice.ind > 0 && ExtremePrice.ind < size/sizeof(uint32_t) && now < ExtremePrice.timestamp+3600 ) + { + fprintf(stderr,"cmp dir.%d PriceCache[0][ExtremePrice.ind] %u >= %u ExtremePrice.pricebits\n",ExtremePrice.dir,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits); + if ( (ExtremePrice.dir > 0 && PriceCache[0][ExtremePrice.ind] >= ExtremePrice.pricebits) || (ExtremePrice.dir < 0 && PriceCache[0][ExtremePrice.ind] <= ExtremePrice.pricebits) ) + { + fprintf(stderr,"future price is close enough to allow approving previously rejected block ind.%d %u vs %u\n",ExtremePrice.ind,PriceCache[0][ExtremePrice.ind],ExtremePrice.pricebits); + if ( (pindex= komodo_blockindex(ExtremePrice.blockhash)) != 0 ) + pindex->nStatus &= ~BLOCK_FAILED_MASK; + else fprintf(stderr,"couldnt find block.%s\n",ExtremePrice.blockhash.GetHex().c_str()); + } + } + // high volatility still strands nodes so we need to check new prices to approve a stuck block + // scan list of stuck blocks (one?) and auto reconsiderblock if it changed state + + //int32_t i; for (i=0; i= KOMODO_MAXPRICES ) + return(-1); + mult = komodo_pricemult(ind); + if ( nonzprices != 0 ) + memset(nonzprices,0,sizeof(*nonzprices)*PRICES_DAYWINDOW); + //for (i=0; i= PRICES_DAYWINDOW ) + i = 0; + if ( (price= rawprices[i*rawskip]) == 0 ) + { + fprintf(stderr,"null rawprice.[%d]\n",i); + return(-1); + } + if ( price >= lowprice && price <= highprice ) + { + //fprintf(stderr,"%.1f ",(double)price/10000); + sum += price; + correlation++; + if ( correlation > (PRICES_DAYWINDOW>>1) ) + { + if ( nonzprices == 0 ) + return(refprice * mult); + //fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); + //return(sum*mult/correlation); + n = 0; + i = (iter + seed) % PRICES_DAYWINDOW; + for (k=0; k= PRICES_DAYWINDOW ) + i = 0; + if ( n > (PRICES_DAYWINDOW>>1) ) + nonzprices[i] = 0; + else + { + price = rawprices[i*rawskip]; + if ( price < lowprice || price > highprice ) + nonzprices[i] = 0; + else + { + nonzprices[i] = price; + //fprintf(stderr,"(%d %u) ",i,rawprices[i*rawskip]); + n++; + } + } + } + //fprintf(stderr,"ind.%d iter.%d j.%d i.%d n.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); + if ( n != correlation ) + return(-1); + sum = den = n = 0; + for (i=0; i %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); + return((sum * mult) / den); + } + } + } + if ( correlation > maxcorrelation ) + maxcorrelation = correlation; + } + fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%llu high.%llu low.%llu\n",ind,iter,maxcorrelation,(long long)refprice,(long long)highprice,(long long)lowprice); + return(0); +} + +int64_t _pairave64(int64_t valA,int64_t valB) +{ + if ( valA != 0 && valB != 0 ) + return((valA + valB) / 2); + else if ( valA != 0 ) return(valA); + else return(valB); +} + +int64_t _pairdiff64(register int64_t valA,register int64_t valB) +{ + if ( valA != 0 && valB != 0 ) + return(valA - valB); + else return(0); +} + +int64_t balanced_ave64(int64_t buf[],int32_t i,int32_t width) +{ + register int32_t nonz,j; register int64_t sum,price; + nonz = 0; + sum = 0; + for (j=-width; j<=width; j++) + { + price = buf[i + j]; + if ( price != 0 ) + { + sum += price; + nonz++; + } + } + if ( nonz != 0 ) + sum /= nonz; + return(sum); +} + +void buf_trioave64(int64_t dest[],int64_t src[],int32_t n) +{ + register int32_t i,j,width = 3; + for (i=0; i<128; i++) + src[i] = 0; + //for (i=n-width-1; i>width; i--) + // dest[i] = balanced_ave(src,i,width); + //for (i=width; i>0; i--) + // dest[i] = balanced_ave(src,i,i); + for (i=1; i *(int64_t *)b) return 1; + else if ( (uint64_t)a < (uint64_t)b ) // jl777 prevent nondeterminism + return(-1); + else return(1); +} + +static int64_t sort64(int64_t *l, int32_t llen) +{ + qsort(l,llen,sizeof(uint64_t),cmp_llu); +} + +static int revcmp_llu(const void *a, const void*b) +{ + if(*(int64_t *)a < *(int64_t *)b) return 1; + else if(*(int64_t *)a > *(int64_t *)b) return -1; + else if ( (uint64_t)a < (uint64_t)b ) // jl777 prevent nondeterminism + return(-1); + else return(1); +} + +static int64_t revsort64(int64_t *l, int32_t llen) +{ + qsort(l,llen,sizeof(uint64_t),revcmp_llu); +} + +int64_t komodo_priceave(int64_t *buf,int64_t *correlated,int32_t cskip) +{ + int32_t i,dir=0; int64_t sum=0,nonzprice,price,halfave,thirdave,fourthave,decayprice; + if ( PRICES_DAYWINDOW < 2 ) + return(0); + for (i=0; i price ) // rising prices + sort64(buf,PRICES_DAYWINDOW); + else revsort64(buf,PRICES_DAYWINDOW); + decayprice = buf[0]; + for (i=0; i %.8f\n",halfave 0 && PRICES[0].fp != 0 && createflag != 0 ) + { + fseek(PRICES[0].fp,(2*PRICES_DAYWINDOW+PRICES_SMOOTHWIDTH) * sizeof(uint32_t) * i,SEEK_SET); + fputc(0,PRICES[0].fp); + fflush(PRICES[0].fp); + } + fprintf(stderr,"pricesinit done i.%d num.%d numprices.%d\n",i,num,(int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET)/sizeof(uint32_t))); + if ( i != num || i != komodo_cbopretsize(ASSETCHAINS_CBOPRET)/sizeof(uint32_t) ) + { + fprintf(stderr,"fatal error opening prices files, start shutdown\n"); + StartShutdown(); + } + return(0); +} + +pthread_mutex_t pricemutex; + +// PRICES file layouts +// [0] rawprice32 / timestamp +// [1] correlated +// [2] 24hr ave +// [3] to [7] reserved + +void komodo_pricesupdate(int32_t height,CBlock *pblock) +{ + static int numprices; static uint32_t *ptr32; static int64_t *ptr64,*tmpbuf; + int32_t ind,offset,width; int64_t correlated,smoothed; uint64_t seed,rngval; uint32_t rawprices[KOMODO_MAXPRICES],buf[PRICES_MAXDATAPOINTS*2]; + width = PRICES_DAYWINDOW;//(2*PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH); + if ( numprices == 0 ) + { + pthread_mutex_init(&pricemutex,0); + numprices = (int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET) / sizeof(uint32_t)); + ptr32 = (uint32_t *)calloc(sizeof(uint32_t),numprices * width); + ptr64 = (int64_t *)calloc(sizeof(int64_t),PRICES_DAYWINDOW*PRICES_MAXDATAPOINTS); + tmpbuf = (int64_t *)calloc(sizeof(int64_t),2*PRICES_DAYWINDOW); + fprintf(stderr,"prices update: numprices.%d %p %p\n",numprices,ptr32,ptr64); + } + if ( _komodo_heightpricebits(&seed,rawprices,pblock) == numprices ) + { + //for (ind=0; ind PRICES_DAYWINDOW ) + { + fseek(PRICES[0].fp,(height-width+1) * numprices * sizeof(uint32_t),SEEK_SET); + if ( fread(ptr32,sizeof(uint32_t),width*numprices,PRICES[0].fp) == width*numprices ) + { + rngval = seed; + for (ind=1; ind 0 ) + { + fseek(PRICES[ind].fp,height * sizeof(int64_t) * PRICES_MAXDATAPOINTS,SEEK_SET); + memset(buf,0,sizeof(buf)); + buf[0] = rawprices[ind]; + buf[1] = rawprices[0]; // timestamp + memcpy(&buf[2],&correlated,sizeof(correlated)); + if ( fwrite(buf,1,sizeof(buf),PRICES[ind].fp) != sizeof(buf) ) + fprintf(stderr,"error fwrite buf for ht.%d ind.%d\n",height,ind); + else if ( height > PRICES_DAYWINDOW*2 ) + { + fseek(PRICES[ind].fp,(height-PRICES_DAYWINDOW+1) * PRICES_MAXDATAPOINTS * sizeof(int64_t),SEEK_SET); + if ( fread(ptr64,sizeof(int64_t),PRICES_DAYWINDOW*PRICES_MAXDATAPOINTS,PRICES[ind].fp) == PRICES_DAYWINDOW*PRICES_MAXDATAPOINTS ) + { + if ( (smoothed= komodo_priceave(tmpbuf,&ptr64[(PRICES_DAYWINDOW-1)*PRICES_MAXDATAPOINTS+1],-PRICES_MAXDATAPOINTS)) > 0 ) + { + fseek(PRICES[ind].fp,(height * PRICES_MAXDATAPOINTS + 2) * sizeof(int64_t),SEEK_SET); + if ( fwrite(&smoothed,1,sizeof(smoothed),PRICES[ind].fp) != sizeof(smoothed) ) + fprintf(stderr,"error fwrite smoothed for ht.%d ind.%d\n",height,ind); + else fflush(PRICES[ind].fp); + } else fprintf(stderr,"error price_smoothed ht.%d ind.%d\n",height,ind); + } else fprintf(stderr,"error fread ptr64 for ht.%d ind.%d\n",height,ind); + } + } else fprintf(stderr,"error komodo_pricecorrelated for ht.%d ind.%d\n",height,ind); + } + fprintf(stderr,"height.%d\n",height); + } else fprintf(stderr,"error reading rawprices for ht.%d\n",height); + } else fprintf(stderr,"height.%d <= width.%d\n",height,width); + pthread_mutex_unlock(&pricemutex); + } else fprintf(stderr,"null PRICES[0].fp\n"); + } else fprintf(stderr,"numprices mismatch, height.%d\n",height); +} + +int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks) +{ + FILE *fp; int32_t retval = PRICES_MAXDATAPOINTS; + pthread_mutex_lock(&pricemutex); + if ( ind < KOMODO_MAXPRICES && (fp= PRICES[ind].fp) != 0 ) + { + fseek(fp,height * PRICES_MAXDATAPOINTS * sizeof(int64_t),SEEK_SET); + if ( fread(buf64,sizeof(int64_t),numblocks*PRICES_MAXDATAPOINTS,fp) != numblocks*PRICES_MAXDATAPOINTS ) + retval = -1; + } + pthread_mutex_unlock(&pricemutex); + return(retval); +} + diff --git a/src/komodo_globals.h b/src/komodo_globals.h index 79b56cbbb..1ba137edc 100644 --- a/src/komodo_globals.h +++ b/src/komodo_globals.h @@ -29,9 +29,8 @@ uint64_t komodo_paxtotal(); int32_t komodo_longestchain(); uint64_t komodo_maxallowed(int32_t baseid); int32_t komodo_bannedset(int32_t *indallvoutsp,uint256 *array,int32_t max); -bool pubkey2addr(char *destaddr,uint8_t *pubkey33); -pthread_mutex_t komodo_mutex; +pthread_mutex_t komodo_mutex,staked_mutex; #define KOMODO_ELECTION_GAP 2000 //((ASSETCHAINS_SYMBOL[0] == 0) ? 2000 : 100) #define KOMODO_ASSETCHAIN_MAXLEN 65 @@ -46,15 +45,18 @@ struct komodo_state KOMODO_STATES[34]; int COINBASE_MATURITY = _COINBASE_MATURITY;//100; unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10; -int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS; +int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,STAKED_ERA,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS; int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1; -std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; +std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB; uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEYHASH[20],ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE,ASSETCHAINS_TXPOW,ASSETCHAINS_MARMARA; bool VERUS_MINTBLOCKS; +std::vector Mineropret; +std::vector vWhiteListAddress; +char NOTARYADDRS[64][64]; char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096]; uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_BEAMPORT,ASSETCHAINS_CODAPORT; -uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT,KOMODO_DPOWCONFS = 1; +uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC,KOMODO_STOPAT,KOMODO_DPOWCONFS = 1,STAKING_MIN_DIFF; uint32_t ASSETCHAINS_MAGIC = 2387029918; int64_t ASSETCHAINS_GENESISTXVAL = 5000000000; @@ -67,22 +69,26 @@ int64_t MAX_MONEY = 200000000 * 100000000LL; // spec will use an op_return with CLTV at front and anything after |OP_RETURN|PUSH of rest|OPRETTYPE_TIMELOCK|script| #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff uint64_t ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF; -uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0; +uint64_t ASSETCHAINS_TIMEUNLOCKFROM = 0, ASSETCHAINS_TIMEUNLOCKTO = 0,ASSETCHAINS_CBOPRET=0; -uint32_t ASSETCHAINS_LASTERA = 1; -uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS]; +uint64_t ASSETCHAINS_LASTERA = 1; +uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_DECAY[ASSETCHAINS_MAX_ERAS],ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS]; uint8_t ASSETCHAINS_CCDISABLES[256]; +std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; #define _ASSETCHAINS_EQUIHASH 0 -uint32_t ASSETCHAINS_NUMALGOS = 2; +uint32_t ASSETCHAINS_NUMALGOS = 3; uint32_t ASSETCHAINS_EQUIHASH = _ASSETCHAINS_EQUIHASH; uint32_t ASSETCHAINS_VERUSHASH = 1; -const char *ASSETCHAINS_ALGORITHMS[] = {"equihash", "verushash"}; -uint64_t ASSETCHAINS_NONCEMASK[] = {0xffff,0xfffffff}; -uint32_t ASSETCHAINS_NONCESHIFT[] = {32,16}; -uint32_t ASSETCHAINS_HASHESPERROUND[] = {1,4096}; +uint32_t ASSETCHAINS_VERUSHASHV1_1 = 2; +const char *ASSETCHAINS_ALGORITHMS[] = {"equihash", "verushash", "verushash11"}; +uint64_t ASSETCHAINS_NONCEMASK[] = {0xffff,0xfffffff,0xfffffff}; +uint32_t ASSETCHAINS_NONCESHIFT[] = {32,16,16}; +uint32_t ASSETCHAINS_HASHESPERROUND[] = {1,4096,4096}; uint32_t ASSETCHAINS_ALGO = _ASSETCHAINS_EQUIHASH; - +// min diff returned from GetNextWorkRequired needs to be added here for each algo, so they can work with ac_staked. +uint32_t ASSETCHAINS_MINDIFF[] = {537857807,504303375,487526159}; + // ^ wrong! // Verus proof of stake controls int32_t ASSETCHAINS_LWMAPOS = 0; // percentage of blocks should be PoS int32_t VERUS_BLOCK_POSUNITS = 1024; // one block is 1000 units @@ -94,7 +100,8 @@ int32_t ASSETCHAINS_SAPLING = -1; int32_t ASSETCHAINS_OVERWINTER = -1; uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; -uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY = 10; +int32_t ASSETCHAINS_STAKED; +uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY = 10,ASSETCHAINS_FOUNDERS_REWARD; uint32_t KOMODO_INITDONE; char KMDUSERPASS[8192+512+1],BTCUSERPASS[8192]; uint16_t KMD_PORT = 7771,BITCOIND_RPCPORT = 7771; @@ -126,20 +133,20 @@ int32_t komodo_baseid(char *origbase) #ifndef SATOSHIDEN #define SATOSHIDEN ((uint64_t)100000000L) #endif -int64_t komodo_current_supply(uint32_t nHeight) +uint64_t komodo_current_supply(uint32_t nHeight) { uint64_t cur_money; int32_t baseid; - if ( (baseid = komodo_baseid(ASSETCHAINS_SYMBOL)) >= 0 && baseid < 32 ) - cur_money = ASSETCHAINS_GENESISTXVAL + ASSETCHAINS_SUPPLY + nHeight * ASSETCHAINS_REWARD[0] / SATOSHIDEN; - else + //if ( (baseid = komodo_baseid(ASSETCHAINS_SYMBOL)) >= 0 && baseid < 32 ) + // cur_money = ASSETCHAINS_GENESISTXVAL + ASSETCHAINS_SUPPLY + nHeight * ASSETCHAINS_REWARD[0] / SATOSHIDEN; + //else { // figure out max_money by adding up supply to a maximum of 10,000,000 blocks cur_money = (ASSETCHAINS_SUPPLY+1) * SATOSHIDEN + (ASSETCHAINS_MAGIC & 0xffffff) + ASSETCHAINS_GENESISTXVAL; if ( ASSETCHAINS_LASTERA == 0 && ASSETCHAINS_REWARD[0] == 0 ) { - cur_money += (nHeight * 10000) / SATOSHIDEN; + cur_money += (nHeight * 10000);// / SATOSHIDEN; } else { @@ -152,6 +159,8 @@ int64_t komodo_current_supply(uint32_t nHeight) // add rewards from this era, up to nHeight int64_t reward = ASSETCHAINS_REWARD[j]; + + //fprintf(stderr,"last.%d reward %llu period %llu\n",(int32_t)ASSETCHAINS_LASTERA,(long long)reward,(long long)ASSETCHAINS_HALVING[j]); if ( reward > 0 ) { uint64_t lastEnd = j == 0 ? 0 : ASSETCHAINS_ENDSUBSIDY[j - 1]; @@ -163,17 +172,19 @@ int64_t komodo_current_supply(uint32_t nHeight) uint32_t modulo = (curEnd - lastEnd) % period; uint64_t decay = ASSETCHAINS_DECAY[j]; - if (!period) + //fprintf(stderr,"period.%llu cur_money %.8f += %.8f * %d\n",(long long)period,(double)cur_money/COIN,(double)reward/COIN,nHeight); + if ( ASSETCHAINS_HALVING[j] == 0 ) { // no halving, straight multiply cur_money += reward * (nHeight - 1); + //fprintf(stderr,"cur_money %.8f\n",(double)cur_money/COIN); } // if exactly SATOSHIDEN, linear decay to zero or to next era, same as: // (next_era_reward + (starting reward - next_era_reward) / 2) * num_blocks else if ( decay == SATOSHIDEN ) { int64_t lowestSubsidy, subsidyDifference, stepDifference, stepTriangle; - int64_t denominator, modulo; + int64_t denominator, modulo=1; int32_t sign = 1; if ( j == ASSETCHAINS_LASTERA ) @@ -260,6 +271,18 @@ int64_t komodo_current_supply(uint32_t nHeight) } } } + } + if ( KOMODO_BIT63SET(cur_money) != 0 ) + return(KOMODO_MAXNVALUE); + if ( ASSETCHAINS_COMMISSION != 0 ) + { + uint64_t newval = (cur_money + (cur_money/COIN * ASSETCHAINS_COMMISSION)); + if ( KOMODO_BIT63SET(newval) != 0 ) + return(KOMODO_MAXNVALUE); + else if ( newval < cur_money ) // check for underflow + return(KOMODO_MAXNVALUE); + return(newval); } - return((int64_t)(cur_money + (cur_money * ASSETCHAINS_COMMISSION))); + //fprintf(stderr,"cur_money %.8f\n",(double)cur_money/COIN); + return(cur_money); } diff --git a/src/komodo_jumblr.h b/src/komodo_jumblr.h old mode 100755 new mode 100644 diff --git a/src/komodo_notary.h b/src/komodo_notary.h index fca7fd223..5cdbf237d 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -15,9 +15,10 @@ #include "komodo_defs.h" - #include "komodo_cJSON.h" +#include "notaries_staked.h" + #define KOMODO_MAINNET_START 178999 const char *Notaries_genesis[][2] = @@ -203,19 +204,25 @@ int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestam { static uint8_t elected_pubkeys0[64][33],elected_pubkeys1[64][33],did0,did1; static int32_t n0,n1; int32_t i,htind,n; uint64_t mask = 0; struct knotary_entry *kp,*tmp; + if ( timestamp == 0 && ASSETCHAINS_SYMBOL[0] != 0 ) timestamp = komodo_heightstamp(height); else if ( ASSETCHAINS_SYMBOL[0] == 0 ) timestamp = 0; - if ( height >= KOMODO_NOTARIES_HARDCODED || ASSETCHAINS_SYMBOL[0] != 0 ) + + // If this chain is not a staked chain, use the normal Komodo logic to determine notaries. This allows KMD to still sync and use its proper pubkeys for dPoW. + if (is_STAKED(ASSETCHAINS_SYMBOL) == 0) { + if ( height >= KOMODO_NOTARIES_HARDCODED || ASSETCHAINS_SYMBOL[0] != 0 ) + timestamp = 0; if ( (timestamp != 0 && timestamp <= KOMODO_NOTARIES_TIMESTAMP1) || (ASSETCHAINS_SYMBOL[0] == 0 && height <= KOMODO_NOTARIES_HEIGHT1) ) { if ( did0 == 0 ) { n0 = (int32_t)(sizeof(Notaries_elected0)/sizeof(*Notaries_elected0)); - for (i=0; i= KOMODO_MAXBLOCKS / KOMODO_ELECTION_GAP ) htind = (KOMODO_MAXBLOCKS / KOMODO_ELECTION_GAP) - 1; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index 96c9cd922..bbd68ae30 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1047,7 +1047,7 @@ uint64_t komodo_block_prg(uint32_t nHeight) { int i; uint8_t hashSrc[8]; - uint64_t result, hashSrc64 = (uint64_t)ASSETCHAINS_MAGIC << 32 + nHeight; + uint64_t result=0, hashSrc64 = (uint64_t)ASSETCHAINS_MAGIC << 32 + nHeight; bits256 hashResult; for ( i = 0; i < sizeof(hashSrc); i++ ) @@ -1366,7 +1366,8 @@ void komodo_statefname(char *fname,char *symbol,char *str) fname[len - n] = 0; else { - printf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,ASSETCHAINS_SYMBOL,n,len,&fname[len - n]); + if ( strcmp(symbol,"REGTEST") != 0 ) + printf("unexpected fname.(%s) vs %s [%s] n.%d len.%d (%s)\n",fname,symbol,ASSETCHAINS_SYMBOL,n,len,&fname[len - n]); return; } } @@ -1538,7 +1539,8 @@ uint16_t komodo_port(char *symbol,uint64_t supply,uint32_t *magicp,uint8_t *extr printf("ports\n"); }*/ -char *iguanafmtstr = (char *)"curl --url \"http://127.0.0.1:7776\" --data \"{\\\"conf\\\":\\\"%s.conf\\\",\\\"path\\\":\\\"${HOME#\"/\"}/.komodo/%s\\\",\\\"unitval\\\":\\\"20\\\",\\\"zcash\\\":1,\\\"RELAY\\\":-1,\\\"VALIDATE\\\":0,\\\"prefetchlag\\\":-1,\\\"poll\\\":100,\\\"active\\\":1,\\\"sapling\\\":1,\\\"agent\\\":\\\"iguana\\\",\\\"method\\\":\\\"addcoin\\\",\\\"startpend\\\":4,\\\"endpend\\\":4,\\\"services\\\":129,\\\"maxpeers\\\":8,\\\"newcoin\\\":\\\"%s\\\",\\\"name\\\":\\\"%s\\\",\\\"hasheaders\\\":1,\\\"useaddmultisig\\\":0,\\\"netmagic\\\":\\\"%s\\\",\\\"p2p\\\":%u,\\\"rpc\\\":%u,\\\"pubval\\\":60,\\\"p2shval\\\":85,\\\"wifval\\\":188,\\\"txfee_satoshis\\\":\\\"10000\\\",\\\"isPoS\\\":0,\\\"minoutput\\\":10000,\\\"minconfirms\\\":2,\\\"genesishash\\\":\\\"027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71\\\",\\\"protover\\\":170002,\\\"genesisblock\\\":\\\"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000000000000000000000000000000000000000000000000000000000000029ab5f490f0f0f200b00000000000000000000000000000000000000000000000000000000000000fd4005000d5ba7cda5d473947263bf194285317179d2b0d307119c2e7cc4bd8ac456f0774bd52b0cd9249be9d40718b6397a4c7bbd8f2b3272fed2823cd2af4bd1632200ba4bf796727d6347b225f670f292343274cc35099466f5fb5f0cd1c105121b28213d15db2ed7bdba490b4cedc69742a57b7c25af24485e523aadbb77a0144fc76f79ef73bd8530d42b9f3b9bed1c135ad1fe152923fafe98f95f76f1615e64c4abb1137f4c31b218ba2782bc15534788dda2cc08a0ee2987c8b27ff41bd4e31cd5fb5643dfe862c9a02ca9f90c8c51a6671d681d04ad47e4b53b1518d4befafefe8cadfb912f3d03051b1efbf1dfe37b56e93a741d8dfd80d576ca250bee55fab1311fc7b3255977558cdda6f7d6f875306e43a14413facdaed2f46093e0ef1e8f8a963e1632dcbeebd8e49fd16b57d49b08f9762de89157c65233f60c8e38a1f503a48c555f8ec45dedecd574a37601323c27be597b956343107f8bd80f3a925afaf30811df83c402116bb9c1e5231c70fff899a7c82f73c902ba54da53cc459b7bf1113db65cc8f6914d3618560ea69abd13658fa7b6af92d374d6eca9529f8bd565166e4fcbf2a8dfb3c9b69539d4d2ee2e9321b85b331925df195915f2757637c2805e1d4131e1ad9ef9bc1bb1c732d8dba4738716d351ab30c996c8657bab39567ee3b29c6d054b711495c0d52e1cd5d8e55b4f0f0325b97369280755b46a02afd54be4ddd9f77c22272b8bbb17ff5118fedbae2564524e797bd28b5f74f7079d532ccc059807989f94d267f47e724b3f1ecfe00ec9e6541c961080d8891251b84b4480bc292f6a180bea089fef5bbda56e1e41390d7c0e85ba0ef530f7177413481a226465a36ef6afe1e2bca69d2078712b3912bba1a99b1fbff0d355d6ffe726d2bb6fbc103c4ac5756e5bee6e47e17424ebcbf1b63d8cb90ce2e40198b4f4198689daea254307e52a25562f4c1455340f0ffeb10f9d8e914775e37d0edca019fb1b9c6ef81255ed86bc51c5391e0591480f66e2d88c5f4fd7277697968656a9b113ab97f874fdd5f2465e5559533e01ba13ef4a8f7a21d02c30c8ded68e8c54603ab9c8084ef6d9eb4e92c75b078539e2ae786ebab6dab73a09e0aa9ac575bcefb29e930ae656e58bcb513f7e3c17e079dce4f05b5dbc18c2a872b22509740ebe6a3903e00ad1abc55076441862643f93606e3dc35e8d9f2caef3ee6be14d513b2e062b21d0061de3bd56881713a1a5c17f5ace05e1ec09da53f99442df175a49bd154aa96e4949decd52fed79ccf7ccbce32941419c314e374e4a396ac553e17b5340336a1a25c22f9e42a243ba5404450b650acfc826a6e432971ace776e15719515e1634ceb9a4a35061b668c74998d3dfb5827f6238ec015377e6f9c94f38108768cf6e5c8b132e0303fb5a200368f845ad9d46343035a6ff94031df8d8309415bb3f6cd5ede9c135fdabcc030599858d803c0f85be7661c88984d88faa3d26fb0e9aac0056a53f1b5d0baed713c853c4a2726869a0a124a8a5bbc0fc0ef80c8ae4cb53636aa02503b86a1eb9836fcc259823e2692d921d88e1ffc1e6cb2bde43939ceb3f32a611686f539f8f7c9f0bf00381f743607d40960f06d347d1cd8ac8a51969c25e37150efdf7aa4c2037a2fd0516fb444525ab157a0ed0a7412b2fa69b217fe397263153782c0f64351fbdf2678fa0dc8569912dcd8e3ccad38f34f23bbbce14c6a26ac24911b308b82c7e43062d180baeac4ba7153858365c72c63dcf5f6a5b08070b730adb017aeae925b7d0439979e2679f45ed2f25a7edcfd2fb77a8794630285ccb0a071f5cce410b46dbf9750b0354aae8b65574501cc69efb5b6a43444074fee116641bb29da56c2b4a7f456991fc92b2\\\",\\\"debug\\\":0,\\\"seedipaddr\\\":\\\"%s\\\"}\""; +char *iguanafmtstr = (char *)"curl --url \"http://127.0.0.1:7776\" --data \"{\\\"conf\\\":\\\"%s.conf\\\",\\\"path\\\":\\\"${HOME#\"/\"}/.komodo/%s\\\",\\\"unitval\\\":\\\"20\\\",\\\"zcash\\\":1,\\\"RELAY\\\":-1,\\\"VALIDATE\\\":0,\\\"prefetchlag\\\":-1,\\\"poll\\\":100,\\\"active\\\":1,\\\"agent\\\":\\\"iguana\\\",\\\"method\\\":\\\"addcoin\\\",\\\"startpend\\\":4,\\\"endpend\\\":4,\\\"services\\\":129,\\\"maxpeers\\\":8,\\\"newcoin\\\":\\\"%s\\\",\\\"name\\\":\\\"%s\\\",\\\"hasheaders\\\":1,\\\"useaddmultisig\\\":0,\\\"netmagic\\\":\\\"%s\\\",\\\"p2p\\\":%u,\\\"rpc\\\":%u,\\\"pubval\\\":60,\\\"p2shval\\\":85,\\\"wifval\\\":188,\\\"txfee_satoshis\\\":\\\"10000\\\",\\\"isPoS\\\":0,\\\"minoutput\\\":10000,\\\"minconfirms\\\":2,\\\"genesishash\\\":\\\"027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71\\\",\\\"protover\\\":170002,\\\"genesisblock\\\":\\\"0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a000000000000000000000000000000000000000000000000000000000000000029ab5f490f0f0f200b00000000000000000000000000000000000000000000000000000000000000fd4005000d5ba7cda5d473947263bf194285317179d2b0d307119c2e7cc4bd8ac456f0774bd52b0cd9249be9d40718b6397a4c7bbd8f2b3272fed2823cd2af4bd1632200ba4bf796727d6347b225f670f292343274cc35099466f5fb5f0cd1c105121b28213d15db2ed7bdba490b4cedc69742a57b7c25af24485e523aadbb77a0144fc76f79ef73bd8530d42b9f3b9bed1c135ad1fe152923fafe98f95f76f1615e64c4abb1137f4c31b218ba2782bc15534788dda2cc08a0ee2987c8b27ff41bd4e31cd5fb5643dfe862c9a02ca9f90c8c51a6671d681d04ad47e4b53b1518d4befafefe8cadfb912f3d03051b1efbf1dfe37b56e93a741d8dfd80d576ca250bee55fab1311fc7b3255977558cdda6f7d6f875306e43a14413facdaed2f46093e0ef1e8f8a963e1632dcbeebd8e49fd16b57d49b08f9762de89157c65233f60c8e38a1f503a48c555f8ec45dedecd574a37601323c27be597b956343107f8bd80f3a925afaf30811df83c402116bb9c1e5231c70fff899a7c82f73c902ba54da53cc459b7bf1113db65cc8f6914d3618560ea69abd13658fa7b6af92d374d6eca9529f8bd565166e4fcbf2a8dfb3c9b69539d4d2ee2e9321b85b331925df195915f2757637c2805e1d4131e1ad9ef9bc1bb1c732d8dba4738716d351ab30c996c8657bab39567ee3b29c6d054b711495c0d52e1cd5d8e55b4f0f0325b97369280755b46a02afd54be4ddd9f77c22272b8bbb17ff5118fedbae2564524e797bd28b5f74f7079d532ccc059807989f94d267f47e724b3f1ecfe00ec9e6541c961080d8891251b84b4480bc292f6a180bea089fef5bbda56e1e41390d7c0e85ba0ef530f7177413481a226465a36ef6afe1e2bca69d2078712b3912bba1a99b1fbff0d355d6ffe726d2bb6fbc103c4ac5756e5bee6e47e17424ebcbf1b63d8cb90ce2e40198b4f4198689daea254307e52a25562f4c1455340f0ffeb10f9d8e914775e37d0edca019fb1b9c6ef81255ed86bc51c5391e0591480f66e2d88c5f4fd7277697968656a9b113ab97f874fdd5f2465e5559533e01ba13ef4a8f7a21d02c30c8ded68e8c54603ab9c8084ef6d9eb4e92c75b078539e2ae786ebab6dab73a09e0aa9ac575bcefb29e930ae656e58bcb513f7e3c17e079dce4f05b5dbc18c2a872b22509740ebe6a3903e00ad1abc55076441862643f93606e3dc35e8d9f2caef3ee6be14d513b2e062b21d0061de3bd56881713a1a5c17f5ace05e1ec09da53f99442df175a49bd154aa96e4949decd52fed79ccf7ccbce32941419c314e374e4a396ac553e17b5340336a1a25c22f9e42a243ba5404450b650acfc826a6e432971ace776e15719515e1634ceb9a4a35061b668c74998d3dfb5827f6238ec015377e6f9c94f38108768cf6e5c8b132e0303fb5a200368f845ad9d46343035a6ff94031df8d8309415bb3f6cd5ede9c135fdabcc030599858d803c0f85be7661c88984d88faa3d26fb0e9aac0056a53f1b5d0baed713c853c4a2726869a0a124a8a5bbc0fc0ef80c8ae4cb53636aa02503b86a1eb9836fcc259823e2692d921d88e1ffc1e6cb2bde43939ceb3f32a611686f539f8f7c9f0bf00381f743607d40960f06d347d1cd8ac8a51969c25e37150efdf7aa4c2037a2fd0516fb444525ab157a0ed0a7412b2fa69b217fe397263153782c0f64351fbdf2678fa0dc8569912dcd8e3ccad38f34f23bbbce14c6a26ac24911b308b82c7e43062d180baeac4ba7153858365c72c63dcf5f6a5b08070b730adb017aeae925b7d0439979e2679f45ed2f25a7edcfd2fb77a8794630285ccb0a071f5cce410b46dbf9750b0354aae8b65574501cc69efb5b6a43444074fee116641bb29da56c2b4a7f456991fc92b2\\\",\\\"debug\\\":0,\\\"seedipaddr\\\":\\\"%s\\\",\\\"sapling\\\":1}\""; + int32_t komodo_whoami(char *pubkeystr,int32_t height,uint32_t timestamp) @@ -1561,7 +1563,7 @@ char *argv0names[] = (char *)"MNZ", (char *)"MNZ", (char *)"MNZ", (char *)"MNZ", (char *)"BTCH", (char *)"BTCH", (char *)"BTCH", (char *)"BTCH" }; -int64_t komodo_max_money() +uint64_t komodo_max_money() { return komodo_current_supply(10000000); } @@ -1638,32 +1640,49 @@ uint64_t komodo_ac_block_subsidy(int nHeight) } } } - if ( nHeight == 1 ) + uint32_t magicExtra = ASSETCHAINS_STAKED ? ASSETCHAINS_MAGIC : (ASSETCHAINS_MAGIC & 0xffffff); + if ( ASSETCHAINS_SUPPLY > 10000000000 ) // over 10 billion? + { + if ( nHeight <= ASSETCHAINS_SUPPLY/1000000000 ) + { + subsidy += (uint64_t)1000000000 * COIN; + if ( nHeight == 1 ) + subsidy += (ASSETCHAINS_SUPPLY % 1000000000)*COIN + magicExtra; + } + } + else if ( nHeight == 1 ) { - uint32_t magicExtra = ASSETCHAINS_STAKED ? ASSETCHAINS_MAGIC : (ASSETCHAINS_MAGIC & 0xffffff); if ( ASSETCHAINS_LASTERA == 0 ) subsidy = ASSETCHAINS_SUPPLY * SATOSHIDEN + magicExtra; else subsidy += ASSETCHAINS_SUPPLY * SATOSHIDEN + magicExtra; } + else if ( is_STAKED(ASSETCHAINS_SYMBOL) == 2 ) + return(0); + // LABS fungible chains, cannot have any block reward! return(subsidy); } extern int64_t MAX_MONEY; +void komodo_cbopretupdate(int32_t forceflag); +void SplitStr(const std::string& strVal, std::vector &outVals); void komodo_args(char *argv0) { extern const char *Notaries_elected1[][2]; - std::string name,addn; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; + std::string name,addn,hexstr,symbol; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[32756],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); + IS_STAKED_NOTARY = GetArg("-stakednotary", -1); + if ( IS_STAKED_NOTARY != -1 && IS_KOMODO_NOTARY == true ) { + fprintf(stderr, "Cannot be STAKED and KMD notary at the same time!\n"); + StartShutdown(); + } memset(ccenables,0,sizeof(ccenables)); memset(disablebits,0,sizeof(disablebits)); if ( GetBoolArg("-gen", false) != 0 ) { KOMODO_MININGTHREADS = GetArg("-genproclimit",-1); } - else KOMODO_MININGTHREADS = 0; - if ( (KOMODO_EXCHANGEWALLET= GetBoolArg("-exchange", false)) != 0 ) fprintf(stderr,"KOMODO_EXCHANGEWALLET mode active\n"); DONATION_PUBKEY = GetArg("-donation", ""); @@ -1671,6 +1690,7 @@ void komodo_args(char *argv0) KOMODO_DEALERNODE = GetArg("-dealer",0); if ( strlen(NOTARY_PUBKEY.c_str()) == 66 ) { + decode_hex(NOTARY_PUBKEY33,33,(char *)NOTARY_PUBKEY.c_str()); USE_EXTERNAL_PUBKEY = 1; if ( IS_KOMODO_NOTARY == 0 ) { @@ -1680,13 +1700,13 @@ void komodo_args(char *argv0) IS_KOMODO_NOTARY = 1; KOMODO_MININGTHREADS = 1; mapArgs ["-genproclimit"] = itostr(KOMODO_MININGTHREADS); + IS_STAKED_NOTARY = -1; fprintf(stderr,"running as notary.%d %s\n",i,Notaries_elected1[i][0]); break; } } - //KOMODO_PAX = 1; - } //else KOMODO_PAX = GetArg("-pax",0); - name = GetArg("-ac_name",""); + } + name = GetArg("-ac_name",""); if ( argv0 != 0 ) { len = (int32_t)strlen(argv0); @@ -1706,6 +1726,7 @@ void komodo_args(char *argv0) WITNESS_CACHE_SIZE = MAX_REORG_LENGTH+10; ASSETCHAINS_CC = GetArg("-ac_cc",0); KOMODO_CCACTIVATE = GetArg("-ac_ccactivate",0); + ASSETCHAINS_BLOCKTIME = GetArg("-ac_blocktime",60); ASSETCHAINS_PUBLIC = GetArg("-ac_public",0); ASSETCHAINS_PRIVATE = GetArg("-ac_private",0); if ( (KOMODO_REWIND= GetArg("-rewind",0)) != 0 ) @@ -1721,6 +1742,7 @@ void komodo_args(char *argv0) if (std::string(ASSETCHAINS_ALGORITHMS[i]) == selectedAlgo) { ASSETCHAINS_ALGO = i; + STAKING_MIN_DIFF = ASSETCHAINS_MINDIFF[i]; // only worth mentioning if it's not equihash if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH) printf("ASSETCHAINS_ALGO, algorithm set to %s\n", selectedAlgo.c_str()); @@ -1736,7 +1758,7 @@ void komodo_args(char *argv0) if ( ASSETCHAINS_LASTERA < 1 || ASSETCHAINS_LASTERA > ASSETCHAINS_MAX_ERAS ) { ASSETCHAINS_LASTERA = 1; - printf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %u\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA); + printf("ASSETCHAINS_LASTERA, if specified, must be between 1 and %u. ASSETCHAINS_LASTERA set to %lu\n", ASSETCHAINS_MAX_ERAS, ASSETCHAINS_LASTERA); } ASSETCHAINS_LASTERA -= 1; @@ -1754,6 +1776,7 @@ void komodo_args(char *argv0) Split(GetArg("-ac_reward",""), ASSETCHAINS_REWARD, 0); Split(GetArg("-ac_halving",""), ASSETCHAINS_HALVING, 0); Split(GetArg("-ac_decay",""), ASSETCHAINS_DECAY, 0); + Split(GetArg("-ac_notarypay",""), ASSETCHAINS_NOTARY_PAY, 0); for ( int i = 0; i < ASSETCHAINS_MAX_ERAS; i++ ) { @@ -1772,13 +1795,52 @@ void komodo_args(char *argv0) MAX_BLOCK_SIGOPS = 60000; ASSETCHAINS_TXPOW = GetArg("-ac_txpow",0) & 3; ASSETCHAINS_FOUNDERS = GetArg("-ac_founders",0);// & 1; + ASSETCHAINS_FOUNDERS_REWARD = GetArg("-ac_founders_reward",0); ASSETCHAINS_SUPPLY = GetArg("-ac_supply",10); + if ( ASSETCHAINS_SUPPLY > (uint64_t)90*1000*1000000 ) + { + fprintf(stderr,"-ac_supply must be less than 90 billion\n"); + StartShutdown(); + } + fprintf(stderr,"ASSETCHAINS_SUPPLY %llu\n",(long long)ASSETCHAINS_SUPPLY); + ASSETCHAINS_COMMISSION = GetArg("-ac_perc",0); ASSETCHAINS_OVERRIDE_PUBKEY = GetArg("-ac_pubkey",""); ASSETCHAINS_SCRIPTPUB = GetArg("-ac_script",""); ASSETCHAINS_BEAMPORT = GetArg("-ac_beam",0); ASSETCHAINS_CODAPORT = GetArg("-ac_coda",0); ASSETCHAINS_MARMARA = GetArg("-ac_marmara",0); + ASSETCHAINS_CBOPRET = GetArg("-ac_cbopret",0); + //fprintf(stderr,"ASSETCHAINS_CBOPRET.%llx\n",(long long)ASSETCHAINS_CBOPRET); + if ( ASSETCHAINS_CBOPRET != 0 ) + { + SplitStr(GetArg("-ac_prices",""), ASSETCHAINS_PRICES); + if ( ASSETCHAINS_PRICES.size() > 0 ) + ASSETCHAINS_CBOPRET |= 4; + SplitStr(GetArg("-ac_stocks",""), ASSETCHAINS_STOCKS); + if ( ASSETCHAINS_STOCKS.size() > 0 ) + ASSETCHAINS_CBOPRET |= 8; + for (i=0; i 0 ) + { + for (i=first; i<=last; i++) + { + CLEARBIT(disablebits,i); + ASSETCHAINS_CCDISABLES[i] = 0; + } + }*/ } if ( ASSETCHAINS_BEAMPORT != 0 && ASSETCHAINS_CODAPORT != 0 ) { fprintf(stderr,"can only have one of -ac_beam or -ac_coda\n"); - exit(0); + StartShutdown(); } ASSETCHAINS_SELFIMPORT = GetArg("-ac_import",""); // BEAM, CODA, PUBKEY, GATEWAY if ( ASSETCHAINS_SELFIMPORT == "PUBKEY" ) @@ -1817,22 +1887,22 @@ void komodo_args(char *argv0) if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) != 66 ) { fprintf(stderr,"invalid -ac_pubkey for -ac_import=PUBKEY\n"); - exit(0); + StartShutdown(); } } else if ( ASSETCHAINS_SELFIMPORT == "BEAM" && ASSETCHAINS_BEAMPORT == 0 ) { fprintf(stderr,"missing -ac_beam for BEAM rpcport\n"); - exit(0); + StartShutdown(); } else if ( ASSETCHAINS_SELFIMPORT == "CODA" && ASSETCHAINS_CODAPORT == 0 ) { fprintf(stderr,"missing -ac_coda for CODA rpcport\n"); - exit(0); + StartShutdown(); } // else it can be gateway coin - + if ( (ASSETCHAINS_STAKED= GetArg("-ac_staked",0)) > 100 ) ASSETCHAINS_STAKED = 100; @@ -1853,18 +1923,32 @@ void komodo_args(char *argv0) } if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 || ASSETCHAINS_SCRIPTPUB.size() > 1 ) { + if ( ASSETCHAINS_SUPPLY > 10000000000 ) + { + printf("ac_pubkey or ac_script wont work with ac_supply over 10 billion\n"); + StartShutdown(); + } + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) + { + printf("Assetchains NOTARY PAY cannot be used with ac_pubkey or ac_script.\n"); + StartShutdown(); + } if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 ) { decode_hex(ASSETCHAINS_OVERRIDE_PUBKEY33,33,(char *)ASSETCHAINS_OVERRIDE_PUBKEY.c_str()); calc_rmd160_sha256(ASSETCHAINS_OVERRIDE_PUBKEYHASH,ASSETCHAINS_OVERRIDE_PUBKEY33,33); } - if ( ASSETCHAINS_COMMISSION == 0 ) + if ( ASSETCHAINS_COMMISSION == 0 && ASSETCHAINS_FOUNDERS != 0 ) { - if (ASSETCHAINS_FOUNDERS != 0 ) + if ( ASSETCHAINS_FOUNDERS_REWARD == 0 ) { ASSETCHAINS_COMMISSION = 53846154; // maps to 35% printf("ASSETCHAINS_COMMISSION defaulted to 35%% when founders reward active\n"); } + else + { + printf("ASSETCHAINS_FOUNDERS_REWARD set to %ld\n", ASSETCHAINS_FOUNDERS_REWARD); + } /*else if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) { //ASSETCHAINS_OVERRIDE_PUBKEY.clear(); @@ -1877,20 +1961,20 @@ void komodo_args(char *argv0) if ( ASSETCHAINS_COMMISSION != 0 ) { ASSETCHAINS_COMMISSION = 0; - printf("ASSETCHAINS_COMMISSION needs an ASETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n"); + printf("ASSETCHAINS_COMMISSION needs an ASSETCHAINS_OVERRIDE_PUBKEY and cant be more than 100000000 (100%%)\n"); } if ( ASSETCHAINS_FOUNDERS != 0 ) { ASSETCHAINS_FOUNDERS = 0; - printf("ASSETCHAINS_FOUNDERS needs an ASETCHAINS_OVERRIDE_PUBKEY\n"); + printf("ASSETCHAINS_FOUNDERS needs an ASSETCHAINS_OVERRIDE_PUBKEY or ASSETCHAINS_SCRIPTPUB\n"); } } if ( ASSETCHAINS_SCRIPTPUB.size() > 1 && ASSETCHAINS_MARMARA != 0 ) { fprintf(stderr,"-ac_script and -ac_marmara are mutually exclusive\n"); - exit(0); + StartShutdown(); } - if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 ) + if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 ) { fprintf(stderr,"perc %.4f%% ac_pub=[%02x%02x%02x...] acsize.%d\n",dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0],ASSETCHAINS_OVERRIDE_PUBKEY33[1],ASSETCHAINS_OVERRIDE_PUBKEY33[2],(int32_t)ASSETCHAINS_SCRIPTPUB.size()); extraptr = extrabuf; @@ -1899,17 +1983,20 @@ void komodo_args(char *argv0) // if we have one era, this should create the same data structure as it used to, same if we increase _MAX_ERAS for ( int i = 0; i <= ASSETCHAINS_LASTERA; i++ ) { - printf("ERA%u: end.%llu reward.%llu halving.%llu decay.%llu\n", i, + printf("ERA%u: end.%llu reward.%llu halving.%llu decay.%llu notarypay.%llu\n", i, (long long)ASSETCHAINS_ENDSUBSIDY[i], (long long)ASSETCHAINS_REWARD[i], (long long)ASSETCHAINS_HALVING[i], - (long long)ASSETCHAINS_DECAY[i]); + (long long)ASSETCHAINS_DECAY[i], + (long long)ASSETCHAINS_NOTARY_PAY[i]); // TODO: Verify that we don't overrun extrabuf here, which is a 256 byte buffer extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_ENDSUBSIDY[i]),(void *)&ASSETCHAINS_ENDSUBSIDY[i]); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_REWARD[i]),(void *)&ASSETCHAINS_REWARD[i]); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_HALVING[i]),(void *)&ASSETCHAINS_HALVING[i]); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_DECAY[i]),(void *)&ASSETCHAINS_DECAY[i]); + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_NOTARY_PAY[i]),(void *)&ASSETCHAINS_NOTARY_PAY[i]); } if (ASSETCHAINS_LASTERA > 0) @@ -1936,14 +2023,19 @@ void komodo_args(char *argv0) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_LWMAPOS),(void *)&ASSETCHAINS_LWMAPOS); } - val = ASSETCHAINS_COMMISSION | (((uint64_t)ASSETCHAINS_STAKED & 0xff) << 32) | (((uint64_t)ASSETCHAINS_CC & 0xffff) << 40) | ((ASSETCHAINS_PUBLIC != 0) << 7) | ((ASSETCHAINS_PRIVATE != 0) << 6) | ASSETCHAINS_TXPOW; + val = ASSETCHAINS_COMMISSION | (((int64_t)ASSETCHAINS_STAKED & 0xff) << 32) | (((uint64_t)ASSETCHAINS_CC & 0xffff) << 40) | ((ASSETCHAINS_PUBLIC != 0) << 7) | ((ASSETCHAINS_PRIVATE != 0) << 6) | ASSETCHAINS_TXPOW; extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(val),(void *)&val); + if ( ASSETCHAINS_FOUNDERS != 0 ) { uint8_t tmp = 1; extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(tmp),(void *)&tmp); if ( ASSETCHAINS_FOUNDERS > 1 ) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS),(void *)&ASSETCHAINS_FOUNDERS); + if ( ASSETCHAINS_FOUNDERS_REWARD != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS_REWARD),(void *)&ASSETCHAINS_FOUNDERS_REWARD); + } } if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { @@ -1980,6 +2072,38 @@ void komodo_args(char *argv0) } fprintf(stderr," <- CCLIB name\n"); } + if ( ASSETCHAINS_BLOCKTIME != 60 ) + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_BLOCKTIME),(void *)&ASSETCHAINS_BLOCKTIME); + if ( Mineropret.size() != 0 ) + { + for (i=0; i= KOMODO_FIRSTFUNGIBLEID && MAX_MONEY < 1000000LL*SATOSHIDEN ) MAX_MONEY = 1000000LL*SATOSHIDEN; - if ( MAX_MONEY <= 0 || MAX_MONEY > 10000100000LL*SATOSHIDEN ) - MAX_MONEY = 10000100000LL*SATOSHIDEN; - //fprintf(stderr,"MAX_MONEY %llu %.8f\n",(long long)MAX_MONEY,(double)MAX_MONEY/SATOSHIDEN); + if ( KOMODO_BIT63SET(MAX_MONEY) != 0 ) + MAX_MONEY = KOMODO_MAXNVALUE; + fprintf(stderr,"MAX_MONEY %llu %.8f\n",(long long)MAX_MONEY,(double)MAX_MONEY/SATOSHIDEN); //printf("baseid.%d MAX_MONEY.%s %.8f\n",baseid,ASSETCHAINS_SYMBOL,(double)MAX_MONEY/SATOSHIDEN); uint16_t tmpport = komodo_port(ASSETCHAINS_SYMBOL,ASSETCHAINS_SUPPLY,&ASSETCHAINS_MAGIC,extraptr,extralen); if ( GetArg("-port",0) != 0 ) @@ -2011,7 +2135,7 @@ void komodo_args(char *argv0) while ( (dirname= (char *)GetDataDir(false).string().c_str()) == 0 || dirname[0] == 0 ) { - fprintf(stderr,"waiting for datadir\n"); + fprintf(stderr,"waiting for datadir (%s)\n",dirname); #ifndef _WIN32 sleep(3); #else @@ -2026,12 +2150,12 @@ void komodo_args(char *argv0) if ( strcmp(ASSETCHAINS_SYMBOL,"KMD") == 0 ) { fprintf(stderr,"cant have assetchain named KMD\n"); - exit(0); + StartShutdown(); } if ( (port= komodo_userpass(ASSETCHAINS_USERPASS,ASSETCHAINS_SYMBOL)) != 0 ) ASSETCHAINS_RPCPORT = port; else komodo_configfile(ASSETCHAINS_SYMBOL,ASSETCHAINS_P2PPORT + 1); - if (ASSETCHAINS_LASTERA == 0) + if (ASSETCHAINS_LASTERA == 0 || is_STAKED(ASSETCHAINS_SYMBOL) != 0) COINBASE_MATURITY = 1; //fprintf(stderr,"ASSETCHAINS_RPCPORT (%s) %u\n",ASSETCHAINS_SYMBOL,ASSETCHAINS_RPCPORT); } @@ -2104,10 +2228,15 @@ void komodo_args(char *argv0) if ( strcmp("PIRATE",ASSETCHAINS_SYMBOL) == 0 && ASSETCHAINS_HALVING[0] == 77777 ) { ASSETCHAINS_HALVING[0] *= 5; - fprintf(stderr,"PIRATE halving changed to %d %.1f days ASSETCHAINS_LASTERA.%d\n",(int32_t)ASSETCHAINS_HALVING[0],(double)ASSETCHAINS_HALVING[0]/1440,ASSETCHAINS_LASTERA); + fprintf(stderr,"PIRATE halving changed to %d %.1f days ASSETCHAINS_LASTERA.%lu\n",(int32_t)ASSETCHAINS_HALVING[0],(double)ASSETCHAINS_HALVING[0]/1440,ASSETCHAINS_LASTERA); } else if ( strcmp("VRSC",ASSETCHAINS_SYMBOL) == 0 ) dpowconfs = 0; + else if ( ASSETCHAINS_PRIVATE != 0 ) + { + fprintf(stderr,"-ac_private for a non-PIRATE chain is not supported. The only reason to have an -ac_private chain is for total privacy and that is best achieved with the largest anon set. PIRATE has that and it is recommended to just use PIRATE\n"); + StartShutdown(); + } } else BITCOIND_RPCPORT = GetArg("-rpcport", BaseParams().RPCPort()); KOMODO_DPOWCONFS = GetArg("-dpowconfs",dpowconfs); if ( ASSETCHAINS_SYMBOL[0] == 0 || strcmp(ASSETCHAINS_SYMBOL,"SUPERNET") == 0 || strcmp(ASSETCHAINS_SYMBOL,"DEX") == 0 || strcmp(ASSETCHAINS_SYMBOL,"COQUI") == 0 || strcmp(ASSETCHAINS_SYMBOL,"PIRATE") == 0 || strcmp(ASSETCHAINS_SYMBOL,"KMDICE") == 0 ) diff --git a/src/main.cpp b/src/main.cpp index f519cab9d..e0e40c9a5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -19,7 +19,6 @@ ******************************************************************************/ #include "main.h" - #include "sodium.h" #include "addrman.h" @@ -48,6 +47,7 @@ #include "validationinterface.h" #include "wallet/asyncrpcoperation_sendmany.h" #include "wallet/asyncrpcoperation_shieldcoinbase.h" +#include "notaries_staked.h" #include #include @@ -85,6 +85,7 @@ int32_t komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block); //void komodo_broadcast(CBlock *pblock,int32_t limit); bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); void komodo_setactivation(int32_t height); +void komodo_pricesupdate(int32_t height,CBlock *pblock); BlockMap mapBlockIndex; CChain chainActive; @@ -186,6 +187,8 @@ namespace { std::vector vinfoBlockFile,tmpBlockFiles; int nLastBlockFile = 0; int nLastTmpFile = 0; + unsigned int maxTempFileSize0 = MAX_TEMPFILE_SIZE; + unsigned int maxTempFileSize1 = MAX_TEMPFILE_SIZE; /** Global flag to indicate we should check to see if there are * block/undo files that should be deleted. Set on startup * or if we allocate more file space when we're in prune mode @@ -509,6 +512,8 @@ namespace { // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to // download that next block if the window were 1 larger. + if ( ASSETCHAINS_CBOPRET != 0 && IsInitialBlockDownload() == 0 ) + BLOCK_DOWNLOAD_WINDOW = 1; int nWindowEnd = state->pindexLastCommonBlock->GetHeight() + BLOCK_DOWNLOAD_WINDOW; int nMaxHeight = std::min(state->pindexBestKnownBlock->GetHeight(), nWindowEnd + 1); NodeId waitingfor = -1; @@ -975,7 +980,7 @@ unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& in * Ensure that a coinbase transaction is structured according to the consensus rules of the * chain */ -bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeight) +bool ContextualCheckCoinbaseTransaction(const CBlock *block,CBlockIndex * const previndex,const CTransaction& tx, const int nHeight,int32_t validateprices) { // if time locks are on, ensure that this coin base is time locked exactly as it should be if (((uint64_t)(tx.GetValueOut()) >= ASSETCHAINS_TIMELOCKGTE) || @@ -1012,6 +1017,15 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh } return(false); } + else if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) + { + + } + else if ( ASSETCHAINS_CBOPRET != 0 && validateprices != 0 && nHeight > 0 && tx.vout.size() > 0 ) + { + if ( komodo_opretvalidate(block,previndex,nHeight,tx.vout[tx.vout.size()-1].scriptPubKey) < 0 ) + return(false); + } return(true); } @@ -1024,12 +1038,12 @@ bool ContextualCheckCoinbaseTransaction(const CTransaction& tx, const int nHeigh * and ContextualCheckBlock (which calls this function). * 3. The isInitBlockDownload argument is only to assist with testing. */ -bool ContextualCheckTransaction( +bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const previndex, const CTransaction& tx, CValidationState &state, const int nHeight, const int dosLevel, - bool (*isInitBlockDownload)()) + bool (*isInitBlockDownload)(),int32_t validateprices) { bool overwinterActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); bool saplingActive = NetworkUpgradeActive(nHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING); @@ -1108,7 +1122,9 @@ bool ContextualCheckTransaction( if (IsExpiredTx(tx, nHeight)) { // Don't increase banscore if the transaction only just expired int expiredDosLevel = IsExpiredTx(tx, nHeight - 1) ? (dosLevel > 10 ? dosLevel : 10) : 0; - return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired"); + string strHex = EncodeHexTx(tx); + //fprintf(stderr, "transaction exipred.%s\n",strHex.c_str()); + return state.DoS(expiredDosLevel, error("ContextualCheckTransaction(): transaction %s is expired, expiry block %i vs current block %i\n txhex.%s",tx.GetHash().ToString(),tx.nExpiryHeight,nHeight,strHex), REJECT_INVALID, "tx-overwinter-expired"); } } @@ -1158,7 +1174,7 @@ bool ContextualCheckTransaction( if (tx.IsCoinBase()) { - if (!ContextualCheckCoinbaseTransaction(tx, nHeight)) + if (!ContextualCheckCoinbaseTransaction(block,previndex,tx, nHeight,validateprices)) return state.DoS(100, error("CheckTransaction(): invalid script data for coinbase time lock"), REJECT_INVALID, "bad-txns-invalid-script-data-for-coinbase-time-lock"); } @@ -1274,8 +1290,8 @@ int32_t komodo_isnotaryvout(char *coinaddr) // from ac_private chains only didinit = 1; } for (i=0; i<=sizeof(Notaries_elected1)/sizeof(*Notaries_elected1); i++) - if ( strcmp(coinaddr,notaryaddrs[i]) == 0 ) - return(1); + if ( strcmp(coinaddr,notaryaddrs[i]) == 0 ) + return(1); return(0); } @@ -1329,9 +1345,10 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio // Transactions containing empty `vin` must have either non-empty // `vjoinsplit` or non-empty `vShieldedSpend`. - if (tx.vin.empty() && tx.vjoinsplit.empty() && tx.vShieldedSpend.empty()) + if (tx.vin.empty() && tx.vjoinsplit.empty() && tx.vShieldedSpend.empty()) return state.DoS(10, error("CheckTransaction(): vin empty"), REJECT_INVALID, "bad-txns-vin-empty"); + // Transactions containing empty `vout` must have either non-empty // `vjoinsplit` or non-empty `vShieldedOutput`. if (tx.vout.empty() && tx.vjoinsplit.empty() && tx.vShieldedOutput.empty()) @@ -1375,7 +1392,7 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio } } if ( txout.scriptPubKey.size() > IGUANA_MAXSCRIPTSIZE ) - return state.DoS(100, error("CheckTransaction(): txout.scriptPubKey.size() too big"),REJECT_INVALID, "bad-txns-vout-negative"); + return state.DoS(100, error("CheckTransaction(): txout.scriptPubKey.size() too big"),REJECT_INVALID, "bad-txns-opret-too-big"); nValueOut += txout.nValue; if (!MoneyRange(nValueOut)) return state.DoS(100, error("CheckTransaction(): txout total out of range"), @@ -1588,7 +1605,6 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio CAmount GetMinRelayFee(const CTransaction& tx, unsigned int nBytes, bool fAllowFree) { - extern int32_t KOMODO_ON_DEMAND; { LOCK(mempool.cs); uint256 hash = tx.GetHash(); @@ -1663,7 +1679,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa } // DoS level set to 10 to be more forgiving. // Check transaction contextually against the set of consensus rules which apply in the next block to be mined. - if (!fSkipExpiry && !ContextualCheckTransaction(tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel)) + if (!fSkipExpiry && !ContextualCheckTransaction(0,0,tx, state, nextBlockHeight, (dosLevel == -1) ? 10 : dosLevel,0)) { return error("AcceptToMemoryPool: ContextualCheckTransaction failed"); } @@ -1801,7 +1817,9 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa CAmount nValueOut = tx.GetValueOut(); CAmount nFees = nValueIn-nValueOut; double dPriority = view.GetPriority(tx, chainActive.Height()); - + if ( nValueOut > 777777*COIN && KOMODO_VALUETOOBIG(nValueOut - 777777*COIN) != 0 ) // some room for blockreward and txfees + return state.DoS(100, error("AcceptToMemoryPool: GetValueOut too big"),REJECT_INVALID,"tx valueout is too big"); + // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; @@ -1845,7 +1863,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Continuously rate-limit free (really, very-low-fee) transactions // This mitigates 'penny-flooding' -- sending thousands of free transactions just to // be annoying or make others' transactions take longer to confirm. - if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize)) + if (fLimitFree && nFees < ::minRelayTxFee.GetFee(nSize) && !tx.IsCoinImport()) { static CCriticalSection csFreeLimiter; static double dFreeCount; @@ -2076,14 +2094,14 @@ bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlo 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"); + //fprintf(stderr,"check mempool %s\n",hash.GetHex().c_str()); if (mempool.lookup(hash, txOut)) { //fprintf(stderr,"found in mempool\n"); return true; } } - //fprintf(stderr,"check disk\n"); + //fprintf(stderr,"check disk %s\n",hash.GetHex().c_str()); if (fTxIndex) { CDiskTxPos postx; @@ -2105,11 +2123,11 @@ bool myGetTransaction(const uint256 &hash, CTransaction &txOut, uint256 &hashBlo hashBlock = header.GetHash(); if (txOut.GetHash() != hash) return error("%s: txid mismatch", __func__); - //fprintf(stderr,"found on disk\n"); + //fprintf(stderr,"found on disk %s\n",hash.GetHex().c_str()); return true; } } - //fprintf(stderr,"not found\n"); + //fprintf(stderr,"not found on disk %s\n",hash.GetHex().c_str()); return false; } @@ -2269,8 +2287,9 @@ bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex,bool checkPOW) extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern uint64_t ASSETCHAINS_ENDSUBSIDY[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_HALVING[ASSETCHAINS_MAX_ERAS]; extern uint32_t ASSETCHAINS_MAGIC; -extern uint64_t ASSETCHAINS_STAKED,ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; +extern uint64_t ASSETCHAINS_LINEAR,ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; extern uint8_t ASSETCHAINS_PUBLIC,ASSETCHAINS_PRIVATE; +extern int32_t ASSETCHAINS_STAKED; CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams) { @@ -2835,7 +2854,7 @@ namespace { hasher << hashBlock; hasher << blockundo; fileout << hasher.GetHash(); - +//fprintf(stderr,"hashBlock.%s hasher.%s\n",hashBlock.GetHex().c_str(),hasher.GetHash().GetHex().c_str()); return true; } @@ -2860,7 +2879,7 @@ namespace { hasher << hashBlock; hasher << blockundo; if (hashChecksum != hasher.GetHash()) - return error("%s: Checksum mismatch", __func__); + return error("%s: %s Checksum mismatch %s vs %s", __func__,hashBlock.GetHex().c_str(),hashChecksum.GetHex().c_str(),hasher.GetHash().GetHex().c_str()); return true; } @@ -2948,6 +2967,33 @@ void DisconnectNotarisations(const CBlock &block) } } +int8_t GetAddressType(const CScript &scriptPubKey, CTxDestination &vDest, txnouttype &txType, vector> &vSols) +{ + int8_t keyType = 0; + // some non-standard types, like time lock coinbases, don't solve, but do extract + if ( (Solver(scriptPubKey, txType, vSols) || ExtractDestination(scriptPubKey, vDest)) ) + { + keyType = 1; + if (vDest.which()) + { + // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned + CKeyID kid; + if (CBitcoinAddress(vDest).GetKeyID(kid)) + { + vSols.push_back(vector(kid.begin(), kid.end())); + } + } + else if (txType == TX_SCRIPTHASH) + { + keyType = 2; + } + else if (txType == TX_CRYPTOCONDITION ) + { + keyType = 3; + } + } + return keyType; +} bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { @@ -2983,20 +3029,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - if ((Solver(out.scriptPubKey, txType, vSols) || ExtractDestination(out.scriptPubKey, vDest)) && txType != TX_MULTISIG) { - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } + int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) + { for (auto addr : vSols) { uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); @@ -3053,23 +3088,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - // some non-standard types, like time lock coinbases, don't solve, but do extract - if ((Solver(prevout.scriptPubKey, txType, vSols) || ExtractDestination(prevout.scriptPubKey, vDest))) + int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) { - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); @@ -3248,20 +3269,54 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); int32_t futureblock; + CAmount blockReward = 0; // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in if (!CheckBlock(&futureblock,pindex->GetHeight(),pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck) || futureblock != 0 ) { //fprintf(stderr,"checkblock failure in connectblock futureblock.%d\n",futureblock); return false; } - // check pindex->CONTEXT_VALIDATED flag - if ( fCheckPOW != 0 && !ContextualCheckBlock(block, state, pindex->pprev) ) // Activate Jan 15th, 2019 + if ( fCheckPOW != 0 && (pindex->nStatus & BLOCK_VALID_CONTEXT) != BLOCK_VALID_CONTEXT ) // Activate Jan 15th, 2019 { - fprintf(stderr,"ContextualCheckBlock failed ht.%d\n",(int32_t)pindex->GetHeight()); - if ( pindex->nTime > 1547510400 ) - return false; - fprintf(stderr,"grandfathered exception, until jan 15th 2019\n"); + if ( !ContextualCheckBlock(block, state, pindex->pprev) ) + { + fprintf(stderr,"ContextualCheckBlock failed ht.%d\n",(int32_t)pindex->GetHeight()); + if ( pindex->nTime > 1547510400 ) + return false; + fprintf(stderr,"grandfathered exception, until jan 15th 2019\n"); + } else pindex->nStatus |= BLOCK_VALID_CONTEXT; } + + // Do this here before the block is moved to the main block files. + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 && pindex->GetHeight() > 10 ) + { + // do a full block scan to get notarisation position and to enforce a valid notarization is in position 1. + // if notarisation in the block, must be position 1 and the coinbase must pay notaries. + int notarisationTx = komodo_connectblock(true,pindex,*(CBlock *)&block); + // -1 means that the valid notarization isnt in position 1 or there are too many notarizations in this block. + if ( notarisationTx == -1 ) + return state.DoS(100, error("ConnectBlock(): Notarization is not in TX position 1 or block contains more than 1 notarization! Invalid Block!"), + REJECT_INVALID, "bad-notarization-position"); + // 1 means this block contains a valid notarisation and its in position 1. + // its no longer possible for any attempted notarization to be in a block with a valid one! + // if notaries create a notarisation even if its not in this chain it will need to be mined inside its own block! + if ( notarisationTx == 1 ) + { + // Check if the notaries have been paid. + if ( block.vtx[0].vout.size() == 1 ) + return state.DoS(100, error("ConnectBlock(): Notaries have not been paid!"), + REJECT_INVALID, "bad-cb-amount"); + // calculate the notaries compensation and validate the amounts and pubkeys are correct. + uint64_t notarypaycheque = komodo_checknotarypay((CBlock *)&block,(int32_t)pindex->GetHeight()); + fprintf(stderr, "notarypaycheque.%lu\n", notarypaycheque); + if ( notarypaycheque > 0 ) + blockReward += notarypaycheque; + else + return state.DoS(100, error("ConnectBlock(): Notary pay validation failed!"), + REJECT_INVALID, "bad-cb-amount"); + } + } + // Move the block to the main block file, we need this to create the TxIndex in the following loop. if ( (pindex->nStatus & BLOCK_IN_TMPFILE) != 0 ) { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); @@ -3275,7 +3330,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("AcceptBlock(): ReceivedBlockTransactions failed"); setDirtyFileInfo.insert(blockPos.nFile); - fprintf(stderr,"added ht.%d copy of tmpfile to %d.%d\n",pindex->GetHeight(),blockPos.nFile,blockPos.nPos); + //fprintf(stderr,"added ht.%d copy of tmpfile to %d.%d\n",pindex->GetHeight(),blockPos.nFile,blockPos.nPos); } // verify that the view's current state corresponds to the previous block uint256 hashPrevBlock = pindex->pprev == NULL ? uint256() : pindex->pprev->GetBlockHash(); @@ -3332,7 +3387,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTimeStart = GetTimeMicros(); CAmount nFees = 0; int nInputs = 0; - int64_t interest,sum = 0; + uint64_t valueout; + int64_t voutsum = 0,prevsum=0,interest,sum = 0; unsigned int nSigOps = 0; CDiskTxPos pos(pindex->GetBlockPos(), GetSizeOfCompactSize(block.vtx.size())); std::vector > vPos; @@ -3383,6 +3439,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { if (!view.HaveInputs(tx)) { + fprintf(stderr, "Connect Block missing inputs tx_number.%d \nvin txid.%s vout.%d \n",i,tx.vin[0].prevout.hash.ToString().c_str(),tx.vin[0].prevout.n); return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), REJECT_INVALID, "bad-txns-inputs-missingorspent"); } @@ -3390,10 +3447,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (!view.HaveJoinSplitRequirements(tx)) return state.DoS(100, error("ConnectBlock(): JoinSplit requirements not met"), REJECT_INVALID, "bad-txns-joinsplit-requirements-not-met"); + if (fAddressIndex || fSpentIndex) { - for (size_t j = 0; j < tx.vin.size(); j++) { - + for (size_t j = 0; j < tx.vin.size(); j++) + { const CTxIn input = tx.vin[j]; const CTxOut &prevout = view.GetOutputFor(tx.vin[j]); @@ -3401,25 +3459,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; uint160 addrHash; - int keyType = 0; - // some non-standard types, like time lock coinbases, don't solve, but do extract - if ((Solver(prevout.scriptPubKey, txType, vSols) || ExtractDestination(prevout.scriptPubKey, vDest))) + int keyType = GetAddressType(prevout.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) { - keyType = 1; - - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); @@ -3429,12 +3471,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // remove address from unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue())); } - } - if (fSpentIndex) { - // add the spent index to determine the txid and input that spent an output - // and to find the amount and address from an input - spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->GetHeight(), prevout.nValue, keyType, addrHash))); + if (fSpentIndex) { + // add the spent index to determine the txid and input that spent an output + // and to find the amount and address from an input + spentIndex.push_back(make_pair(CSpentIndexKey(input.prevout.hash, input.prevout.n), CSpentIndexValue(txhash, j, pindex->GetHeight(), prevout.nValue, keyType, addrHash))); + } } } } @@ -3449,9 +3491,25 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin txdata.emplace_back(tx); + valueout = tx.GetValueOut(); + if ( KOMODO_VALUETOOBIG(valueout) != 0 ) + { + fprintf(stderr,"valueout %.8f too big\n",(double)valueout/COIN); + return state.DoS(100, error("ConnectBlock(): GetValueOut too big"),REJECT_INVALID,"tx valueout is too big"); + } + prevsum = voutsum; + voutsum += valueout; + /*if ( KOMODO_VALUETOOBIG(voutsum) != 0 ) + { + fprintf(stderr,"voutsum %.8f too big\n",(double)voutsum/COIN); + return state.DoS(100, error("ConnectBlock(): voutsum too big"),REJECT_INVALID,"tx valueout is too big"); + } + else + if ( voutsum < prevsum ) // PRLPAY overflows this and it isnt a conclusive test anyway + return state.DoS(100, error("ConnectBlock(): voutsum less after adding valueout"),REJECT_INVALID,"tx valueout is too big");*/ if (!tx.IsCoinBase()) { - nFees += view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime) - tx.GetValueOut(); + nFees += view.GetValueIn(chainActive.LastTip()->GetHeight(),&interest,tx,chainActive.LastTip()->nTime) - valueout; sum += interest; std::vector vChecks; @@ -3469,23 +3527,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin vector> vSols; CTxDestination vDest; txnouttype txType = TX_PUBKEYHASH; - int keyType = 1; - // some non-standard types, like time lock coinbases, don't solve, but do extract - if ((Solver(out.scriptPubKey, txType, vSols) || ExtractDestination(out.scriptPubKey, vDest)) && txType != TX_MULTISIG) + int keyType = GetAddressType(out.scriptPubKey, vDest, txType, vSols); + if ( keyType != 0 ) { - // if we failed to solve, and got a vDest, assume P2PKH or P2PK address returned - if (vDest.which()) - { - CKeyID kid; - if (CBitcoinAddress(vDest).GetKeyID(kid)) - { - vSols.push_back(vector(kid.begin(), kid.end())); - } - } - else if (txType == TX_SCRIPTHASH) - { - keyType = 2; - } for (auto addr : vSols) { addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); @@ -3542,8 +3586,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin int64_t nTime1 = GetTimeMicros(); nTimeConnect += nTime1 - nTimeStart; LogPrint("bench", " - Connect %u transactions: %.2fms (%.3fms/tx, %.3fms/txin) [%.2fs]\n", (unsigned)block.vtx.size(), 0.001 * (nTime1 - nTimeStart), 0.001 * (nTime1 - nTimeStart) / block.vtx.size(), nInputs <= 1 ? 0 : 0.001 * (nTime1 - nTimeStart) / (nInputs-1), nTimeConnect * 0.000001); - CAmount blockReward = nFees + GetBlockSubsidy(pindex->GetHeight(), chainparams.GetConsensus()) + sum; - if ( ASSETCHAINS_COMMISSION != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && + blockReward += nFees + GetBlockSubsidy(pindex->GetHeight(), chainparams.GetConsensus()) + sum; + if ( ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 && { uint64_t checktoshis; if ( (checktoshis= komodo_commission((CBlock *)&block,(int32_t)pindex->GetHeight())) != 0 ) @@ -3563,6 +3607,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { if ( ASSETCHAINS_SYMBOL[0] != 0 || pindex->GetHeight() >= KOMODO_NOTARIES_HEIGHT1 || block.vtx[0].vout[0].nValue > blockReward ) { + //fprintf(stderr, "coinbase pays too much\n"); + //sleepflag = true; return state.DoS(100, error("ConnectBlock(): coinbase pays too much (actual=%d vs limit=%d)", block.vtx[0].GetValueOut(), blockReward), @@ -3579,9 +3625,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return true; // Write undo information to disk + //fprintf(stderr,"nFile.%d isNull %d vs isvalid %d nStatus %x\n",(int32_t)pindex->nFile,pindex->GetUndoPos().IsNull(),pindex->IsValid(BLOCK_VALID_SCRIPTS),(uint32_t)pindex->nStatus); if (pindex->GetUndoPos().IsNull() || !pindex->IsValid(BLOCK_VALID_SCRIPTS)) { - if (pindex->GetUndoPos().IsNull()) { + if (pindex->GetUndoPos().IsNull()) + { CDiskBlockPos pos; if (!FindUndoPos(state, pindex->nFile, pos, ::GetSerializeSize(blockundo, SER_DISK, CLIENT_VERSION) + 40)) return error("ConnectBlock(): FindUndoPos failed"); @@ -3589,12 +3637,11 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin fprintf(stderr,"ConnectBlock: unexpected null pprev\n"); if (!UndoWriteToDisk(blockundo, pos, pindex->pprev->GetBlockHash(), chainparams.MessageStart())) return AbortNode(state, "Failed to write undo data"); - // update nUndoPos in block index pindex->nUndoPos = pos.nPos; pindex->nStatus |= BLOCK_HAVE_UNDO; } - + // Now that all consensus rules have been validated, set nCachedBranchId. // Move this if BLOCK_VALID_CONSENSUS is ever altered. static_assert(BLOCK_VALID_CONSENSUS == BLOCK_VALID_SCRIPTS, @@ -3605,12 +3652,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } else if (pindex->pprev) { pindex->nCachedBranchId = pindex->pprev->nCachedBranchId; } - + pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); setDirtyBlockIndex.insert(pindex); } - ConnectNotarisations(block, pindex->GetHeight()); + ConnectNotarisations(block, pindex->GetHeight()); // MoMoM notarisation DB. if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) @@ -3666,7 +3713,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin LogPrint("bench", " - Callbacks: %.2fms [%.2fs]\n", 0.001 * (nTime4 - nTime3), nTimeCallbacks * 0.000001); //FlushStateToDisk(); - komodo_connectblock(pindex,*(CBlock *)&block); + komodo_connectblock(false,pindex,*(CBlock *)&block); // dPoW state update. return true; } @@ -3736,7 +3783,8 @@ bool static FlushStateToDisk(CValidationState &state, FlushStateMode mode) { std::vector > vFiles; vFiles.reserve(setDirtyFileInfo.size()); for (set::iterator it = setDirtyFileInfo.begin(); it != setDirtyFileInfo.end(); ) { - vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); + if ( *it < TMPFILE_START ) + vFiles.push_back(make_pair(*it, &vinfoBlockFile[*it])); setDirtyFileInfo.erase(it++); } std::vector vBlocks; @@ -3854,12 +3902,13 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { return AbortNode(state, "Failed to read block"); //if ( ASSETCHAINS_SYMBOL[0] != 0 || pindexDelete->GetHeight() > 1400000 ) { - int32_t prevMoMheight; uint256 notarizedhash,txid; - komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); + int32_t notarizedht,prevMoMheight; uint256 notarizedhash,txid; + notarizedht = komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); if ( block.GetHash() == notarizedhash ) { fprintf(stderr,"DisconnectTip trying to disconnect notarized block at ht.%d\n",(int32_t)pindexDelete->GetHeight()); - return(false); + return state.DoS(100, error("AcceptBlock(): DisconnectTip trying to disconnect notarized blockht.%d",(int32_t)pindexDelete->GetHeight()), + REJECT_INVALID, "past-notarized-height"); } } // Apply the block atomically to the chain state. @@ -3921,13 +3970,17 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { assert(pcoinsTip->GetSaplingAnchorAt(pcoinsTip->GetBestAnchor(SAPLING), newSaplingTree)); // Let wallets know transactions went from 1-confirmed to // 0-confirmed or conflicted: + std::vector TxToRemove; for (int i = 0; i < block.vtx.size(); i++) { CTransaction &tx = block.vtx[i]; //if ((i == (block.vtx.size() - 1)) && ((ASSETCHAINS_LWMAPOS && block.IsVerusPOSBlock()) || (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block) != 0)))) if ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight()) != 0))) { - EraseFromWallets(tx.GetHash()); +#ifdef ENABLE_WALLET + LOCK2(cs_main, pwalletMain->cs_wallet); + pwalletMain->EraseFromWallet(tx.GetHash()); +#endif } else { @@ -4043,7 +4096,14 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * GetMainSignals().BlockChecked(*pblock, state); if (!rv) { if (state.IsInvalid()) + { InvalidBlockFound(pindexNew, state); + /*if ( ASSETCHAINS_CBOPRET != 0 ) + { + pindexNew->nStatus &= ~BLOCK_FAILED_MASK; + fprintf(stderr,"reconsiderblock %d\n",(int32_t)pindexNew->GetHeight()); + }*/ + } return error("ConnectTip(): ConnectBlock %s failed", pindexNew->GetBlockHash().ToString()); } mapBlockSource.erase(pindexNew->GetBlockHash()); @@ -4092,6 +4152,8 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * komodo_broadcast(pblock,8); else if ( ASSETCHAINS_SYMBOL[0] != 0 ) komodo_broadcast(pblock,4);*/ + if ( ASSETCHAINS_CBOPRET != 0 ) + komodo_pricesupdate(pindexNew->GetHeight(),pblock); if ( ASSETCHAINS_SAPLING <= 0 && pindexNew->nTime > KOMODO_SAPLING_ACTIVATION - 24*3600 ) komodo_activate_sapling(pindexNew); return true; @@ -4171,48 +4233,49 @@ static void PruneBlockIndexCandidates() { * Try to make some progress towards making pindexMostWork the active block. * pblock is either NULL or a pointer to a CBlock corresponding to pindexMostWork. */ -static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) { +static bool ActivateBestChainStep(bool fSkipdpow, CValidationState &state, CBlockIndex *pindexMostWork, CBlock *pblock) { AssertLockHeld(cs_main); bool fInvalidFound = false; const CBlockIndex *pindexOldTip = chainActive.Tip(); const CBlockIndex *pindexFork = chainActive.FindFork(pindexMostWork); + // stop trying to reorg if the reorged chain is before last notarized height. + // stay on the same chain tip! + int32_t notarizedht,prevMoMheight; uint256 notarizedhash,txid; + notarizedht = komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); + if ( !fSkipdpow && pindexFork != 0 && pindexFork->GetHeight() < notarizedht ) + { + fprintf(stderr,"pindexFork->GetHeight().%d is < notarizedht %d, so ignore it\n",(int32_t)pindexFork->GetHeight(),notarizedht); + return state.DoS(100, error("ActivateBestChainStep(): pindexFork->GetHeight().%d is < notarizedht %d, so ignore it",(int32_t)pindexFork->GetHeight(),notarizedht), + REJECT_INVALID, "past-notarized-height"); + } // - On ChainDB initialization, pindexOldTip will be null, so there are no removable blocks. // - If pindexMostWork is in a chain that doesn't have the same genesis block as our chain, // then pindexFork will be null, and we would need to remove the entire chain including // our genesis block. In practice this (probably) won't happen because of checks elsewhere. auto reorgLength = pindexOldTip ? pindexOldTip->GetHeight() - (pindexFork ? pindexFork->GetHeight() : -1) : 0; assert(MAX_REORG_LENGTH > 0);//, "We must be able to reorg some distance"); - if (reorgLength > MAX_REORG_LENGTH) + if ( reorgLength > MAX_REORG_LENGTH) { - int32_t notarizedht,prevMoMheight; uint256 notarizedhash,txid; - notarizedht = komodo_notarized_height(&prevMoMheight,¬arizedhash,&txid); - if ( pindexFork->GetHeight() < notarizedht ) - { - fprintf(stderr,"pindexFork->GetHeight().%d is < notarizedht %d, so ignore it\n",(int32_t)pindexFork->GetHeight(),notarizedht); - pindexFork = pindexOldTip; - } - else - { - auto msg = strprintf(_( - "A block chain reorganization has been detected that would roll back %d blocks! " - "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." - ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + - _("Reorganization details") + ":\n" + - "- " + strprintf(_("Current tip: %s, height %d, work %s\nstake %s"), - pindexOldTip->phashBlock->GetHex(), pindexOldTip->GetHeight(), pindexOldTip->chainPower.chainWork.GetHex(), - pindexOldTip->chainPower.chainStake.GetHex()) + "\n" + - "- " + strprintf(_("New tip: %s, height %d, work %s\nstake %s"), - pindexMostWork->phashBlock->GetHex(), pindexMostWork->GetHeight(), pindexMostWork->chainPower.chainWork.GetHex(), - pindexMostWork->chainPower.chainStake.GetHex()) + "\n" + - "- " + strprintf(_("Fork point: %s %s, height %d"), - ASSETCHAINS_SYMBOL,pindexFork->phashBlock->GetHex(), pindexFork->GetHeight()) + "\n\n" + - _("Please help, human!"); - LogPrintf("*** %s\nif you launch with -maxreorg=%d it might be able to resolve this automatically", msg,reorgLength+10); - uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); - StartShutdown(); - return false; - } + auto msg = strprintf(_( + "A block chain reorganization has been detected that would roll back %d blocks! " + "This is larger than the maximum of %d blocks, and so the node is shutting down for your safety." + ), reorgLength, MAX_REORG_LENGTH) + "\n\n" + + _("Reorganization details") + ":\n" + + "- " + strprintf(_("Current tip: %s, height %d, work %s\nstake %s"), + pindexOldTip->phashBlock->GetHex(), pindexOldTip->GetHeight(), pindexOldTip->chainPower.chainWork.GetHex(), + pindexOldTip->chainPower.chainStake.GetHex()) + "\n" + + "- " + strprintf(_("New tip: %s, height %d, work %s\nstake %s"), + pindexMostWork->phashBlock->GetHex(), pindexMostWork->GetHeight(), pindexMostWork->chainPower.chainWork.GetHex(), + pindexMostWork->chainPower.chainStake.GetHex()) + "\n" + + "- " + strprintf(_("Fork point: %s %s, height %d"), + ASSETCHAINS_SYMBOL,pindexFork->phashBlock->GetHex(), pindexFork->GetHeight()) + "\n\n" + + _("Please help, human!"); + LogPrintf("*** %s\nif you launch with -maxreorg=%d it might be able to resolve this automatically", msg,reorgLength+10); + fprintf(stderr,"*** %s\nif you launch with -maxreorg=%d it might be able to resolve this automatically", msg.c_str(),reorgLength+10); + uiInterface.ThreadSafeMessageBox(msg, "", CClientUIInterface::MSG_ERROR); + StartShutdown(); + return false; } // Disconnect active blocks which are no longer in the best chain. @@ -4305,7 +4368,7 @@ static bool ActivateBestChainStep(CValidationState &state, CBlockIndex *pindexMo * or an activated best chain. pblock is either NULL or a pointer to a block * that is already loaded (to avoid loading it again from disk). */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock) { +bool ActivateBestChain(bool fSkipdpow, CValidationState &state, CBlock *pblock) { CBlockIndex *pindexNewTip = NULL; CBlockIndex *pindexMostWork = NULL; const CChainParams& chainParams = Params(); @@ -4321,7 +4384,7 @@ bool ActivateBestChain(CValidationState &state, CBlock *pblock) { if (pindexMostWork == NULL || pindexMostWork == chainActive.Tip()) return true; - if (!ActivateBestChainStep(state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) + if (!ActivateBestChainStep(fSkipdpow, state, pindexMostWork, pblock && pblock->GetHash() == pindexMostWork->GetBlockHash() ? pblock : NULL)) return false; pindexNewTip = chainActive.Tip(); fInitialDownload = IsInitialBlockDownload(); @@ -4578,12 +4641,20 @@ bool FindBlockPos(int32_t tmpflag,CValidationState &state, CDiskBlockPos &pos, u std::vector *ptr; int *lastfilep; LOCK(cs_LastBlockFile); - unsigned int nFile; + unsigned int nFile,maxTempFileSize; + if ( tmpflag != 0 ) { ptr = &tmpBlockFiles; nFile = nLastTmpFile; lastfilep = &nLastTmpFile; + if (tmpBlockFiles.size() <= nFile) { + tmpBlockFiles.resize(nFile + 1); + } + if ( nFile == 0 ) + maxTempFileSize = maxTempFileSize0; + else if ( nFile == 1 ) + maxTempFileSize = maxTempFileSize1; } else { @@ -4594,25 +4665,89 @@ bool FindBlockPos(int32_t tmpflag,CValidationState &state, CDiskBlockPos &pos, u vinfoBlockFile.resize(nFile + 1); } } + if (!fKnown) { - while ((*ptr)[nFile].nSize + nAddSize >= MAX_BLOCKFILE_SIZE) { + bool tmpfileflag = false; + while ( (*ptr)[nFile].nSize + nAddSize >= ((tmpflag != 0) ? maxTempFileSize : MAX_BLOCKFILE_SIZE) ) { + if ( tmpflag != 0 && tmpfileflag ) + break; nFile++; if ((*ptr).size() <= nFile) { (*ptr).resize(nFile + 1); } + tmpfileflag = true; } pos.nFile = nFile + tmpflag*TMPFILE_START; pos.nPos = (*ptr)[nFile].nSize; - if ( 0 && tmpflag != 0 ) - fprintf(stderr,"pos.nFile %d nPos %u\n",pos.nFile,pos.nPos); } - if (nFile != *lastfilep) { if (!fKnown) { LogPrintf("Leaving block file %i: %s\n", nFile, (*ptr)[nFile].ToString()); } FlushBlockFile(!fKnown); + //fprintf(stderr, "nFile = %i size.%li maxTempFileSize0.%u maxTempFileSize1.%u\n",nFile,tmpBlockFiles.size(),maxTempFileSize0,maxTempFileSize1); + if ( tmpflag != 0 && tmpBlockFiles.size() >= 3 ) + { + if ( nFile == 1 ) // Trying to get to second temp file. + { + if (!PruneOneBlockFile(true,TMPFILE_START+1)) + { + // file 1 is not ready to be used yet increase file 0's size. + fprintf(stderr, "Cant clear file 1!\n"); + // We will reset the position to the end of the first file, even if its over max size. + nFile = 0; + pos.nFile = TMPFILE_START; + pos.nPos = (*ptr)[0].nSize; + // Increase temp file one's max size by a chunk, so we wait a reasonable time to recheck the other file. + maxTempFileSize0 += BLOCKFILE_CHUNK_SIZE; + } + else + { + // The file 1 is able to be used now. Reset max size, and set nfile to use file 1. + fprintf(stderr, "CLEARED file 1!\n"); + maxTempFileSize0 = MAX_TEMPFILE_SIZE; + nFile = 1; + tmpBlockFiles[1].SetNull(); + pos.nFile = TMPFILE_START+1; + pos.nPos = (*ptr)[1].nSize; + boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); + LogPrintf("Prune: deleted temp blk (%05u)\n",nFile); + } + if ( 0 && tmpflag != 0 ) + fprintf(stderr,"pos.nFile %d nPos %u\n",pos.nFile,pos.nPos); + } + else if ( nFile == 2 ) // Trying to get to third temp file. + { + if (!PruneOneBlockFile(true,TMPFILE_START)) + { + fprintf(stderr, "Cant clear file 0!\n"); + // We will reset the position to the end of the second block file, even if its over max size. + nFile = 1; + pos.nFile = TMPFILE_START+1; + pos.nPos = (*ptr)[1].nSize; + // Increase temp file one's max size by a chunk, so we wait a reasonable time to recheck the other file. + maxTempFileSize1 += BLOCKFILE_CHUNK_SIZE; + } + else + { + // The file 0 is able to be used now. Reset max size, and set nfile to use file 0. + fprintf(stderr, "CLEARED file 0!\n"); + maxTempFileSize1 = MAX_TEMPFILE_SIZE; + nFile = 0; + tmpBlockFiles[0].SetNull(); + pos.nFile = TMPFILE_START; + pos.nPos = (*ptr)[0].nSize; + boost::filesystem::remove(GetBlockPosFilename(pos, "blk")); + LogPrintf("Prune: deleted temp blk (%05u)\n",nFile); + } + if ( 0 && tmpflag != 0 ) + fprintf(stderr,"pos.nFile %d nPos %u\n",pos.nFile,pos.nPos); + } + //sleep(30); + } + //fprintf(stderr, "nFile = %i size.%li maxTempFileSize0.%u maxTempFileSize1.%u\n",nFile,tmpBlockFiles.size(),maxTempFileSize0,maxTempFileSize1); sleep(30); *lastfilep = nFile; + //fprintf(stderr, "*lastfilep = %i\n",*lastfilep); } (*ptr)[nFile].AddBlock(nHeight, nTime); @@ -4884,6 +5019,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C if (!CheckTransaction(tiptime,tx, state, verifier)) return error("CheckBlock: CheckTransaction failed"); } + unsigned int nSigOps = 0; BOOST_FOREACH(const CTransaction& tx, block.vtx) { @@ -5015,7 +5151,7 @@ bool ContextualCheckBlock(const CBlock& block, CValidationState& state, CBlockIn const CTransaction& tx = block.vtx[i]; // Check transaction contextually against consensus rules at block height - if (!ContextualCheckTransaction(tx, state, nHeight, 100)) { + if (!ContextualCheckTransaction(&block,pindexPrev,tx, state, nHeight, 100)) { return false; // Failure reason has been set in validation state object } @@ -5060,7 +5196,7 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat miSelf->second = pindex = AddToBlockIndex(block); if (ppindex) *ppindex = pindex; - if ( pindex != 0 && pindex->nStatus & BLOCK_FAILED_MASK ) + if ( pindex != 0 && (pindex->nStatus & BLOCK_FAILED_MASK) != 0 ) { if ( ASSETCHAINS_CC == 0 )//&& (ASSETCHAINS_PRIVATE == 0 || KOMODO_INSYNC >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight) ) return state.Invalid(error("%s: block is marked invalid", __func__), 0, "duplicate"); @@ -5137,6 +5273,8 @@ bool AcceptBlockHeader(int32_t *futureblockp,const CBlockHeader& block, CValidat return true; } +uint256 Queued_reconsiderblock; + bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, CBlockIndex** ppindex, bool fRequested, CDiskBlockPos* dbp) { const CChainParams& chainparams = Params(); @@ -5182,7 +5320,8 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C // See method docstring for why this is always disabled auto verifier = libzcash::ProofVerifier::Disabled(); - if ((!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !ContextualCheckBlock(block, state, pindex->pprev)) + bool fContextualCheckBlock = ContextualCheckBlock(block, state, pindex->pprev); + if ( (!CheckBlock(futureblockp,pindex->GetHeight(),pindex,block, state, verifier,0)) || !fContextualCheckBlock ) { static int32_t saplinght = -1; CBlockIndex *tmpptr; @@ -5196,6 +5335,18 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C fprintf(stderr,"saplinght.%d tipht.%d blockht.%d cmp.%d\n",saplinght,(int32_t)tmpptr->GetHeight(),pindex->GetHeight(),pindex->GetHeight() < 0 || (pindex->GetHeight() >= saplinght && pindex->GetHeight() < saplinght+50000) || (tmpptr->GetHeight() > saplinght-720 && tmpptr->GetHeight() < saplinght+720)); if ( pindex->GetHeight() < 0 || (pindex->GetHeight() >= saplinght && pindex->GetHeight() < saplinght+50000) || (tmpptr->GetHeight() > saplinght-720 && tmpptr->GetHeight() < saplinght+720) ) *futureblockp = 1; + if ( ASSETCHAINS_CBOPRET != 0 ) + { + CValidationState tmpstate; CBlockIndex *tmpindex; int32_t ht,longest; + ht = (int32_t)pindex->GetHeight(); + longest = komodo_longestchain(); + if ( (longest == 0 || ht < longest-6) && (tmpindex=komodo_chainactive(ht)) != 0 ) + { + fprintf(stderr,"reconsider height.%d, longest.%d\n",(int32_t)ht,longest); + if ( Queued_reconsiderblock == zeroid ) + Queued_reconsiderblock = pindex->GetBlockHash(); + } + } } if ( *futureblockp == 0 ) { @@ -5207,9 +5358,16 @@ bool AcceptBlock(int32_t *futureblockp,CBlock& block, CValidationState& state, C return false; } } + if ( fContextualCheckBlock ) + pindex->nStatus |= BLOCK_VALID_CONTEXT; int nHeight = pindex->GetHeight(); + // Temp File fix. LABS has been using this for ages with no bad effects. + // Disabled here. Set use tmp to whatever you need to use this for. int32_t usetmp = 0; + if ( IsInitialBlockDownload() ) + usetmp = 0; + // Write block to history file try { unsigned int nBlockSize = ::GetSerializeSize(block, SER_DISK, CLIENT_VERSION); @@ -5344,6 +5502,27 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo komodo_currentheight_set(chainActive.LastTip()->GetHeight()); checked = CheckBlock(&futureblock,height!=0?height:komodo_block2height(pblock),0,*pblock, state, verifier,0); bool fRequested = MarkBlockAsReceived(hash); + // Test thing on LABS to test viability of rejecting a node pushing a chain. + // Supposed to stop malicious forks being pushed. Untested. Disabled incase of problems it may cause. + if ( 0 && pfrom && !fRequested && vNodes.size() > 1 ) + { + pfrom->nBlocksinARow += 1; + if ( pfrom->nBlocksinARow >= 10 ) + { + pfrom->nBlocksinARow2 += 1; + if ( pfrom->nBlocksinARow2 > 5 ) + { + pfrom->nBlocksinARow = 0; + pfrom->nBlocksinARow2 = 0; + fprintf(stderr, "reset node.%i\n",(int32_t)pfrom->GetId()); + } + else + { + fprintf(stderr, "Requesting new peer node.%i blocksinrow.%i blocsinrow2.%i\n",(int32_t)pfrom->GetId(),pfrom->nBlocksinARow,pfrom->nBlocksinARow2); + return(false); + } + } + } fRequested |= fForceProcessing; if ( checked != 0 && komodo_checkPOW(0,pblock,height) < 0 ) //from_miner && ASSETCHAINS_STAKED == 0 { @@ -5379,7 +5558,7 @@ bool ProcessNewBlock(bool from_miner,int32_t height,CValidationState &state, CNo //else fprintf(stderr,"added block %s %p\n",pindex->GetBlockHash().ToString().c_str(),pindex->pprev); } - if (futureblock == 0 && !ActivateBestChain(state, pblock)) + if (futureblock == 0 && !ActivateBestChain(false, state, pblock)) return error("%s: ActivateBestChain failed", __func__); //fprintf(stderr,"finished ProcessBlock %d\n",(int32_t)chainActive.LastTip()->GetHeight()); @@ -5440,35 +5619,63 @@ uint64_t CalculateCurrentUsage() } /* Prune a block file (modify associated database entries)*/ -void PruneOneBlockFile(const int fileNumber) +bool PruneOneBlockFile(bool tempfile, const int fileNumber) { - for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) { + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + //fprintf(stderr, "pruneblockfile.%i\n",fileNumber); sleep(15); + for (BlockMap::iterator it = mapBlockIndex.begin(); it != mapBlockIndex.end(); ++it) + { CBlockIndex* pindex = it->second; - if (pindex && pindex->nFile == fileNumber) { + if (pindex && pindex->nFile == fileNumber) + { + if ( tempfile && (pindex->nStatus & BLOCK_IN_TMPFILE != 0) ) + { + if ( chainActive.Contains(pindex) ) + { + // Block is in main chain so we cant clear this file! + return(false); + } + fprintf(stderr, "pindex height.%i notarized height.%i \n", pindex->GetHeight(), notarized_height); + if ( pindex->GetHeight() > notarized_height ) // Need to check this, does an invalid block have a height? + { + // This blocks height is not older than last notarization so it can be reorged into the main chain. + // We cant clear this file! + return(false); + } + else + { + // Block is not in main chain and is older than last notarised block so its safe for removal. + fprintf(stderr, "Block [%i] in tempfile.%i We can clear this block!\n",pindex->GetHeight(),fileNumber); + // Add index to list and remove after loop? + } + } pindex->nStatus &= ~BLOCK_HAVE_DATA; pindex->nStatus &= ~BLOCK_HAVE_UNDO; pindex->nFile = 0; pindex->nDataPos = 0; pindex->nUndoPos = 0; setDirtyBlockIndex.insert(pindex); - // Prune from mapBlocksUnlinked -- any block we prune would have // to be downloaded again in order to consider its chain, at which // point it would be considered as a candidate for // mapBlocksUnlinked or setBlockIndexCandidates. std::pair::iterator, std::multimap::iterator> range = mapBlocksUnlinked.equal_range(pindex->pprev); - while (range.first != range.second) { + while (range.first != range.second) + { std::multimap::iterator it = range.first; range.first++; - if (it->second == pindex) { + if (it->second == pindex) + { mapBlocksUnlinked.erase(it); } } } } - - vinfoBlockFile[fileNumber].SetNull(); + if (!tempfile) + vinfoBlockFile[fileNumber].SetNull(); setDirtyFileInfo.insert(fileNumber); + return(true); } @@ -5515,7 +5722,7 @@ void FindFilesToPrune(std::set& setFilesToPrune) if (vinfoBlockFile[fileNumber].nHeightLast > nLastBlockWeCanPrune) continue; - PruneOneBlockFile(fileNumber); + PruneOneBlockFile(false, fileNumber); // Queue up the files for removal setFilesToPrune.insert(fileNumber); nCurrentUsage -= nBytesToPrune; @@ -6128,7 +6335,7 @@ bool InitBlockIndex() { return error("LoadBlockIndex(): couldnt add to block index"); if (!ReceivedBlockTransactions(block, state, pindex, blockPos)) return error("LoadBlockIndex(): genesis block not accepted"); - if (!ActivateBestChain(state, &block)) + if (!ActivateBestChain(true, state, &block)) return error("LoadBlockIndex(): genesis block cannot be activated"); // Force a chainstate write so that when we VerifyDB in a moment, it doesn't check stale data return FlushStateToDisk(state, FLUSH_STATE_ALWAYS); @@ -6689,7 +6896,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, return true; } -//fprintf(stderr,"netmsg: %s\n", strCommand.c_str()); + //fprintf(stderr,"netmsg: %s\n", strCommand.c_str()); if (strCommand == "version") { @@ -6706,16 +6913,18 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, CAddress addrFrom; uint64_t nNonce = 1; int nVersion; // use temporary for version, don't set version number until validated as connected + int minVersion = MIN_PEER_PROTO_VERSION; + if ( is_STAKED(ASSETCHAINS_SYMBOL) != 0 ) + minVersion = STAKEDMIN_PEER_PROTO_VERSION; vRecv >> nVersion >> pfrom->nServices >> nTime >> addrMe; if (nVersion == 10300) nVersion = 300; - - if (nVersion < MIN_PEER_PROTO_VERSION) + if (nVersion < minVersion) { // disconnect from peers older than this proto version LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion); pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE, - strprintf("Version must be %d or greater", MIN_PEER_PROTO_VERSION)); + strprintf("Version must be %d or greater", minVersion)); pfrom->fDisconnect = true; return false; } @@ -6837,8 +7046,19 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, Misbehaving(pfrom->GetId(), 1); return false; } - - + else if ( strCommand == "events" ) + { + int32_t i; + if ( ASSETCHAINS_CCLIB != "gamescc" ) + { + Misbehaving(pfrom->GetId(), 1); + return false; + } + std::vector payload; + vRecv >> payload; + komodo_netevent(payload); + return(true); + } else if (strCommand == "verack") { pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION)); diff --git a/src/main.h b/src/main.h index d19e3aa17..ec36c976b 100644 --- a/src/main.h +++ b/src/main.h @@ -90,7 +90,8 @@ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default for -txexpirydelta, in number of blocks */ static const unsigned int DEFAULT_TX_EXPIRY_DELTA = 20; /** The maximum size of a blk?????.dat file (since 0.8) */ -static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB +static const unsigned int MAX_TEMPFILE_SIZE = 0x1000000; // 16 MiB 0x8000000 /** The pre-allocation chunk size for blk?????.dat files (since 0.8) */ static const unsigned int BLOCKFILE_CHUNK_SIZE = 0x1000000; // 16 MiB /** The pre-allocation chunk size for rev?????.dat files (since 0.8) */ @@ -110,7 +111,7 @@ static const unsigned int MAX_HEADERS_RESULTS = 160; * Larger windows tolerate larger download speed differences between peer, but increase the potential * degree of disordering of blocks on disk (which make reindexing and in the future perhaps pruning * harder). We'll probably want to make this a per-peer adaptive value at some point. */ -static const unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; +static unsigned int BLOCK_DOWNLOAD_WINDOW = 1024; /** Time to wait (in seconds) between writing blocks/block index to disk. */ static const unsigned int DATABASE_WRITE_INTERVAL = 60 * 60; /** Time to wait (in seconds) between flushing chainstate to disk. */ @@ -251,7 +252,7 @@ std::string GetWarnings(const std::string& strFor); /** Retrieve a transaction (from memory pool, or from disk, if possible) */ bool GetTransaction(const uint256 &hash, CTransaction &tx, uint256 &hashBlock, bool fAllowSlow = false); /** Find the best known block, and make it the tip of the block chain */ -bool ActivateBestChain(CValidationState &state, CBlock *pblock = NULL); +bool ActivateBestChain(bool fSkipdpow, CValidationState &state, CBlock *pblock = NULL); CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); /** @@ -705,8 +706,8 @@ bool ContextualCheckInputs(const CTransaction& tx, CValidationState &state, cons std::vector *pvChecks = NULL); /** Check a transaction contextually against a set of consensus rules */ -bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel, - bool (*isInitBlockDownload)() = IsInitialBlockDownload); +bool ContextualCheckTransaction(const CBlock *block, CBlockIndex * const pindexPrev,const CTransaction& tx, CValidationState &state, int nHeight, int dosLevel, + bool (*isInitBlockDownload)() = IsInitialBlockDownload,int32_t validateprices=1); /** Apply the effects of this transaction on the UTXO set represented by view */ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight); @@ -807,6 +808,7 @@ bool WriteBlockToDisk(const CBlock& block, CDiskBlockPos& pos, const CMessageHea bool ReadBlockFromDisk(CBlock& block, const CDiskBlockPos& pos,bool checkPOW); bool ReadBlockFromDisk(CBlock& block, const CBlockIndex* pindex,bool checkPOW); bool RemoveOrphanedBlocks(int32_t notarized_height); +bool PruneOneBlockFile(bool tempfile, const int fileNumber); /** Functions for validating blocks and updating the block tree */ diff --git a/src/metrics.cpp b/src/metrics.cpp index 6b387f7dc..1fa8ebfb5 100644 --- a/src/metrics.cpp +++ b/src/metrics.cpp @@ -136,7 +136,7 @@ int64_t GetUptime() double GetLocalSolPS() { - if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH) + if (ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1) { return miningTimer.rate(nHashCount); } diff --git a/src/miner.cpp b/src/miner.cpp index 7ffcaebc2..f131bc90e 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -55,6 +55,8 @@ #include "sodium.h" +#include "notaries_staked.h" + #include #include #ifdef ENABLE_MINING @@ -128,8 +130,8 @@ void UpdateTime(CBlockHeader* pblock, const Consensus::Params& consensusParams, pblock->nBits = GetNextWorkRequired(pindexPrev, pblock, consensusParams); } } - #include "komodo_defs.h" +#include "cc/CCinclude.h" extern CCriticalSection cs_metrics; void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len); @@ -147,9 +149,13 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits, arith_uint256 &hashResult, uint8_t *utxosig, CPubKey &pk); int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33); int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); +int32_t komodo_is_notarytx(const CTransaction& tx); CScript Marmara_scriptPubKey(int32_t height,CPubKey pk); CScript MarmaraCoinbaseOpret(uint8_t funcid,int32_t height,CPubKey pk); -bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey); +uint64_t komodo_notarypay(CMutableTransaction &txNew, std::vector &NotarisationNotaries, uint32_t timestamp, int32_t height, uint8_t *script, int32_t len); +int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); +int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len); +CScript komodo_mineropret(int32_t nHeight); CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake) { @@ -168,7 +174,9 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } } else pk = _pk; - uint64_t deposits; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params(); + uint64_t deposits,voutsum=0; int32_t isrealtime,kmdheight; uint32_t blocktime; const CChainParams& chainparams = Params(); + bool fNotarisationBlock = false; std::vector NotarisationNotaries; + //fprintf(stderr,"create new block\n"); // Create new block if ( gpucount < 0 ) @@ -211,7 +219,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 // we will attempt to spend any cheats we see CTransaction cheatTx; boost::optional cheatSpend; - uint256 cbHash; + + uint256 cbHash; CBlockIndex* pindexPrev = 0; { @@ -225,6 +234,8 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 const int64_t nMedianTimePast = pindexPrev->GetMedianTimePast(); uint32_t proposedTime = GetAdjustedTime(); + voutsum = GetBlockSubsidy(nHeight,consensusParams) + 10000*COIN; // approx fees + if (proposedTime == nMedianTimePast) { // too fast or stuck, this addresses the too fast issue, while moving @@ -237,6 +248,13 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } } pblock->nTime = GetAdjustedTime(); + // Now we have the block time + height, we can get the active notaries. + int8_t numSN = 0; uint8_t notarypubkeys[64][33] = {0}; + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) + { + // Only use speical miner for notary pay chains. + numSN = komodo_notaries(notarypubkeys, nHeight, pblock->nTime); + } CCoinsViewCache view(pcoinsTip); uint32_t expired; uint64_t commission; @@ -254,6 +272,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 vecPriority.reserve(mempool.mapTx.size() + 1); // now add transactions from the mem pool + int32_t Notarisations = 0; uint64_t txvalue; for (CTxMemPool::indexed_transaction_set::iterator mi = mempool.mapTx.begin(); mi != mempool.mapTx.end(); ++mi) { @@ -268,7 +287,12 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 //fprintf(stderr,"coinbase.%d finaltx.%d expired.%d\n",tx.IsCoinBase(),IsFinalTx(tx, nHeight, nLockTimeCutoff),IsExpiredTx(tx, nHeight)); continue; } - + txvalue = tx.GetValueOut(); + if ( KOMODO_VALUETOOBIG(txvalue) != 0 ) + continue; + if ( KOMODO_VALUETOOBIG(txvalue + voutsum) != 0 ) + continue; + voutsum += txvalue; if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 ) { //fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure nHeight.%d nTime.%u vs locktime.%u\n",nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime); @@ -280,12 +304,18 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 CAmount nTotalIn = 0; bool fMissingInputs = false; bool fNotarisation = false; + std::vector TMP_NotarisationNotaries; if (tx.IsCoinImport()) { - CAmount nValueIn = GetCoinImportValue(tx); + CAmount nValueIn = GetCoinImportValue(tx); // burn amount nTotalIn += nValueIn; - dPriority += (double)nValueIn * 1000; // flat multiplier + dPriority += (double)nValueIn * 1000; // flat multiplier... max = 1e16. } else { + TMP_NotarisationNotaries.clear(); + bool fToCryptoAddress = false; + if ( numSN != 0 && notarypubkeys[0][0] != 0 && komodo_is_notarytx(tx) == 1 ) + fToCryptoAddress = true; + BOOST_FOREACH(const CTxIn& txin, tx.vin) { // Read prev transaction @@ -323,9 +353,33 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 nTotalIn += nValueIn; int nConf = nHeight - coins->nHeight; - + + uint8_t *script; int32_t scriptlen; uint256 hash; CTransaction tx1; + // loop over notaries array and extract index of signers. + if ( fToCryptoAddress && GetTransaction(txin.prevout.hash,tx1,hash,false) ) + { + for (int8_t i = 0; i < numSN; i++) + { + script = (uint8_t *)&tx1.vout[txin.prevout.n].scriptPubKey[0]; + scriptlen = (int32_t)tx1.vout[txin.prevout.n].scriptPubKey.size(); + if ( scriptlen == 35 && script[0] == 33 && script[34] == OP_CHECKSIG && memcmp(script+1,notarypubkeys[i],33) == 0 ) + { + // We can add the index of each notary to vector, and clear it if this notarisation is not valid later on. + TMP_NotarisationNotaries.push_back(i); + } + } + } dPriority += (double)nValueIn * nConf; } + if ( numSN != 0 && TMP_NotarisationNotaries.size() >= numSN / 5 ) + { + // check a notary didnt sign twice (this would be an invalid notarisation later on and cause problems) + std::set checkdupes( TMP_NotarisationNotaries.begin(), TMP_NotarisationNotaries.end() ); + if ( checkdupes.size() != TMP_NotarisationNotaries.size() ) + { + fprintf(stderr, "possible notarisation is signed multiple times by same notary, passed as normal transaction.\n"); + } else fNotarisation = true; + } nTotalIn += tx.GetShieldedValueIn(); } @@ -340,6 +394,40 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 CFeeRate feeRate(nTotalIn-tx.GetValueOut(), nTxSize); + if ( fNotarisation ) + { + // Special miner for notary pay chains. Can only enter this if numSN/notarypubkeys is set higher up. + if ( tx.vout.size() == 2 && tx.vout[1].nValue == 0 ) + { + // Get the OP_RETURN for the notarisation + uint8_t *script = (uint8_t *)&tx.vout[1].scriptPubKey[0]; + int32_t scriptlen = (int32_t)tx.vout[1].scriptPubKey.size(); + if ( script[0] == OP_RETURN ) + { + Notarisations++; + if ( Notarisations > 1 ) + { + fprintf(stderr, "skipping notarization.%d\n",Notarisations); + // Any attempted notarization needs to be in its own block! + continue; + } + int32_t notarizedheight = komodo_getnotarizedheight(pblock->nTime, nHeight, script, scriptlen); + if ( notarizedheight != 0 ) + { + // this is the first one we see, add it to the block as TX1 + NotarisationNotaries = TMP_NotarisationNotaries; + dPriority = 1e16; + fNotarisationBlock = true; + fprintf(stderr, "Notarisation %s set to maximum priority\n",hash.ToString().c_str()); + } + } + } + } + else if ( dPriority == 1e16 ) + { + dPriority -= 10; + // make sure notarisation is tx[1] in block. + } if (porphan) { porphan->dPriority = dPriority; @@ -473,7 +561,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 int32_t stakeHeight = chainActive.Height() + 1; - //LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x\n", nBlockSize,blocktime,pblock->nBits); + //LogPrintf("CreateNewBlock(): total size %u blocktime.%u nBits.%08x stake.%i\n", nBlockSize,blocktime,pblock->nBits,isStake); if ( ASSETCHAINS_SYMBOL[0] != 0 && isStake ) { LEAVE_CRITICAL_SECTION(cs_main); @@ -499,6 +587,21 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 //if ( blocktime > pindexPrev->GetMedianTimePast()+60 ) // blocktime = pindexPrev->GetMedianTimePast() + 60; siglen = komodo_staked(txStaked, pblock->nBits, &blocktime, &txtime, &utxotxid, &utxovout, &utxovalue, utxosig); + // if you skip this check it will create a block too far into the future and not pass ProcessBlock or AcceptBlock. + // This has been moved from the mining loop to save CPU, and to also make ac_staked work with the verus miner. + while ( blocktime-57 > GetAdjustedTime() ) + { + sleep(1); + if ( (rand() % 100) < 1 ) + fprintf(stderr, "%u seconds until elegible, waiting.\n", blocktime-((uint32_t)GetAdjustedTime()+57)); + if ( chainActive.LastTip()->GetHeight() >= stakeHeight ) + { + fprintf(stderr, "Block Arrived, reset staking loop.\n"); + return(0); + } + if( !GetBoolArg("-gen",false) ) + return(0); + } } if ( siglen > 0 ) @@ -512,9 +615,10 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 pblocktemplate->vTxSigOps.push_back(GetLegacySigOpCount(txStaked)); nFees += txfees; pblock->nTime = blocktime; -printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeight()+1,blocktime,(uint32_t)(GetAdjustedTime() - (blocktime-13))); - } else return(0); //fprintf(stderr,"no utxos eligible for staking\n"); + //printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeight()+1,blocktime,(uint32_t)(GetAdjustedTime() - (blocktime-13))); + } else return(0); //fprintf(stderr,"no utxos eligible for staking\n"); } + // Create coinbase tx CMutableTransaction txNew = CreateNewContextualCMutableTransaction(consensusParams, nHeight); txNew.vin.resize(1); @@ -524,7 +628,7 @@ printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeigh txNew.vout.resize(1); txNew.vout[0].scriptPubKey = scriptPubKeyIn; txNew.vout[0].nValue = GetBlockSubsidy(nHeight,consensusParams) + nFees; - + //fprintf(stderr,"mine ht.%d with %.8f\n",nHeight,(double)txNew.vout[0].nValue/COIN); txNew.nExpiryHeight = 0; txNew.nLockTime = std::max(pindexPrev->GetMedianTimePast()+1, GetAdjustedTime()); @@ -541,7 +645,7 @@ printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeigh txNew.vout[1].nValue = 0; txNew.vout[1].scriptPubKey = MarmaraCoinbaseOpret('C',nHeight,pk); } - else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && ASSETCHAINS_COMMISSION != 0 && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 ) + else if ( nHeight > 1 && ASSETCHAINS_SYMBOL[0] != 0 && (ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1) && (ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0) && (commission= komodo_commission((CBlock*)&pblocktemplate->block,(int32_t)nHeight)) != 0 ) { int32_t i; uint8_t *ptr; txNew.vout.resize(2); @@ -584,6 +688,11 @@ printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeigh if (scriptPubKeyIn.IsPayToScriptHash() || scriptPubKeyIn.IsPayToCryptoCondition()) { fprintf(stderr,"CreateNewBlock: attempt to add timelock to pay2sh or pay2cc\n"); + if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) + { + LEAVE_CRITICAL_SECTION(cs_main); + LEAVE_CRITICAL_SECTION(mempool.cs); + } return 0; } @@ -592,8 +701,37 @@ printf("staking PoS ht.%d t%u lag.%u\n",(int32_t)chainActive.LastTip()->GetHeigh txNew.vout[0].scriptPubKey = CScriptExt().PayToScriptHash(CScriptID(opretScript)); txNew.vout[1].scriptPubKey = CScriptExt().OpReturnScript(opretScript, OPRETTYPE_TIMELOCK); txNew.vout[1].nValue = 0; - } // timelocks and commissions are currently incompatible due to validation complexity of the combination - + // timelocks and commissions are currently incompatible due to validation complexity of the combination + } + else if ( fNotarisationBlock && ASSETCHAINS_NOTARY_PAY[0] != 0 && pblock->vtx[1].vout.size() == 2 && pblock->vtx[1].vout[1].nValue == 0 ) + { + // Get the OP_RETURN for the notarisation + uint8_t *script = (uint8_t *)&pblock->vtx[1].vout[1].scriptPubKey[0]; + int32_t scriptlen = (int32_t)pblock->vtx[1].vout[1].scriptPubKey.size(); + if ( script[0] == OP_RETURN ) + { + uint64_t totalsats = komodo_notarypay(txNew, NotarisationNotaries, pblock->nTime, nHeight, script, scriptlen); + if ( totalsats == 0 ) + { + fprintf(stderr, "Could not create notary payment, trying again.\n"); + if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) + { + LEAVE_CRITICAL_SECTION(cs_main); + LEAVE_CRITICAL_SECTION(mempool.cs); + } + return(0); + } + fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats); + } else fprintf(stderr, "vout 2 of notarisation is not OP_RETURN scriptlen.%i\n", scriptlen); + } + if ( ASSETCHAINS_CBOPRET != 0 ) + { + int32_t numv = (int32_t)txNew.vout.size(); + txNew.vout.resize(numv+1); + txNew.vout[numv].nValue = 0; + txNew.vout[numv].scriptPubKey = komodo_mineropret(nHeight); + //printf("autocreate commision/cbopret.%lld vout[%d]\n",(long long)ASSETCHAINS_CBOPRET,(int32_t)txNew.vout.size()); + } pblock->vtx[0] = txNew; pblocktemplate->vTxFees[0] = -nFees; @@ -790,17 +928,33 @@ CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, scriptPubKey.resize(35); ptr = (uint8_t *)pubkey.begin(); scriptPubKey[0] = 33; - for (i=0; i<33; i++) + for (i=0; i<33; i++) { scriptPubKey[i+1] = ptr[i]; + } scriptPubKey[34] = OP_CHECKSIG; - //scriptPubKey = CScript() << ToByteVector(pubkey) << OP_CHECKSIG; } } if ( ASSETCHAINS_MARMARA != 0 && nHeight > 0 && (nHeight & 1) == 0 ) scriptPubKey = Marmara_scriptPubKey(nHeight,pubkey); - if ( ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0 ) - isStake = true; - return CreateNewBlock(pubkey,scriptPubKey, gpucount, isStake); + return CreateNewBlock(pubkey, scriptPubKey, gpucount, isStake); +} + +void komodo_sendmessage(int32_t minpeers,int32_t maxpeers,const char *message,std::vector payload) +{ + int32_t numsent = 0; + LOCK(cs_vNodes); + BOOST_FOREACH(CNode* pnode, vNodes) + { + if ( pnode->hSocket == INVALID_SOCKET ) + continue; + if ( numsent < minpeers || (rand() % 10) == 0 ) + { + //fprintf(stderr,"pushmessage\n"); + pnode->PushMessage(message,payload); + if ( numsent++ > maxpeers ) + break; + } + } } void komodo_broadcast(CBlock *pblock,int32_t limit) @@ -1196,14 +1350,14 @@ void static BitcoinMiner_noeq() miningTimer.start(); #ifdef ENABLE_WALLET - CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, Mining_height, ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0); + CBlockTemplate *ptr = CreateNewBlockWithKey(reservekey, Mining_height, 0, ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0); #else CBlockTemplate *ptr = CreateNewBlockWithKey(); #endif if ( ptr == 0 ) { static uint32_t counter; - if ( counter++ < 10 ) + if ( ASSETCHAINS_STAKED == 0 && counter++ < 10 ) fprintf(stderr,"created illegal block, retry\n"); continue; } @@ -1246,6 +1400,7 @@ void static BitcoinMiner_noeq() pblock->nSolution = solnPlaceholder; savebits = pblock->nBits; arith_uint256 hashTarget = arith_uint256().SetCompact(pblock->nBits); + HASHTarget = arith_uint256().SetCompact(savebits); arith_uint256 mask(ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO]); Mining_start = 0; @@ -1263,31 +1418,53 @@ void static BitcoinMiner_noeq() if ( ASSETCHAINS_STAKED != 0 ) { - int32_t percPoS,z; - hashTarget = komodo_PoWtarget(&percPoS,hashTarget,Mining_height,ASSETCHAINS_STAKED); - for (z=31; z>=0; z--) - fprintf(stderr,"%02x",((uint8_t *)&hashTarget)[z]); - fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%%\n",percPoS,(int32_t)ASSETCHAINS_STAKED); + int32_t percPoS,z; bool fNegative,fOverflow; + HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); + HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); + LogPrintf("Block %d : PoS %d%% vs target %d%%\n", Mining_height, percPoS, (int32_t)ASSETCHAINS_STAKED); } while (true) { arith_uint256 arNonce = UintToArith256(pblock->nNonce); - + int64_t *extraPtr; + + // This seems to be a really bad way to do this, but its better than copy pasting the entire miner function at this stage. CVerusHashWriter ss = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); ss << *((CBlockHeader *)pblock); - int64_t *extraPtr = ss.xI64p(); + if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH ) + extraPtr = ss.xI64p(); CVerusHash &vh = ss.GetState(); uint256 hashResult = uint256(); vh.ClearExtra(); + + CVerusHashV2Writer ss2 = CVerusHashV2Writer(SER_GETHASH, PROTOCOL_VERSION); + ss2 << *((CBlockHeader *)pblock); + if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) + extraPtr = ss2.xI64p(); + CVerusHashV2 &vh2 = ss2.GetState(); + vh2.ClearExtra(); + int64_t i, count = ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1; int64_t hashesToGo = ASSETCHAINS_HASHESPERROUND[ASSETCHAINS_ALGO]; - + if ( ASSETCHAINS_STAKED > 0 && ASSETCHAINS_STAKED < 100 ) + { + if ( KOMODO_MININGTHREADS > 0 ) + hashTarget = HASHTarget_POW; + else + hashTarget = HASHTarget; + } + else if ( ASSETCHAINS_STAKED == 100 && Mining_height > 100 ) + hashTarget = HASHTarget; + // for speed check NONCEMASK at a time for (i = 0; i < count; i++) { *extraPtr = i; - vh.ExtraHash((unsigned char *)&hashResult); + if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH ) + vh.ExtraHash((unsigned char *)&hashResult); + else if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) + vh2.ExtraHash((unsigned char *)&hashResult); if ( UintToArith256(hashResult) <= hashTarget ) { @@ -1376,8 +1553,8 @@ void static BitcoinMiner_noeq() #else printf("%lu mega hashes complete - working\n", (ASSETCHAINS_NONCEMASK[ASSETCHAINS_ALGO] + 1) / 1048576); #endif + pblock->nBits = savebits; break; - } } } @@ -1499,6 +1676,13 @@ void static BitcoinMiner() #endif if ( ptr == 0 ) { + if ( !GetBoolArg("-gen",false)) + { + miningTimer.stop(); + c.disconnect(); + LogPrintf("KomodoMiner terminated\n"); + return; + } static uint32_t counter; if ( counter++ < 10 && ASSETCHAINS_STAKED == 0 ) fprintf(stderr,"created illegal blockB, retry\n"); @@ -1594,17 +1778,14 @@ void static BitcoinMiner() } //else fprintf(stderr,"duplicate at j.%d\n",j); } else Mining_start = 0; } else Mining_start = 0; - if ( ASSETCHAINS_STAKED != 0 ) + + if ( ASSETCHAINS_STAKED > 0 ) { int32_t percPoS,z; bool fNegative,fOverflow; HASHTarget_POW = komodo_PoWtarget(&percPoS,HASHTarget,Mining_height,ASSETCHAINS_STAKED); HASHTarget.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); - if ( ASSETCHAINS_STAKED < 100 && KOMODO_MININGTHREADS == 0 ) - { - for (z=31; z>=0; z--) - fprintf(stderr,"%02x",((uint8_t *)&HASHTarget_POW)[z]); - fprintf(stderr," PoW for staked coin PoS %d%% vs target %d%% ht.%d\n",percPoS,(int32_t)ASSETCHAINS_STAKED,Mining_height); - } + if ( ASSETCHAINS_STAKED < 100 ) + LogPrintf("Block %d : PoS %d%% vs target %d%% \n",Mining_height,percPoS,(int32_t)ASSETCHAINS_STAKED); } gotinvalid = 0; while (true) @@ -1660,7 +1841,7 @@ void static BitcoinMiner() if ( h > hashTarget ) { //if ( ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0 ) - // sleep(1); + // MilliSleep(30); return false; } if ( IS_KOMODO_NOTARY != 0 && B.nTime > GetAdjustedTime() ) @@ -1687,12 +1868,6 @@ void static BitcoinMiner() } else { - while ( B.nTime-57 > GetAdjustedTime() ) - { - sleep(1); - if ( chainActive.LastTip()->GetHeight() >= Mining_height ) - return(false); - } uint256 tmp = B.GetHash(); int32_t z; for (z=31; z>=0; z--) fprintf(stderr,"%02x",((uint8_t *)&tmp)[z]); @@ -1736,7 +1911,6 @@ void static BitcoinMiner() std::lock_guard lock{m_cs}; return cancelSolver; }; - // TODO: factor this out into a function with the same API for each solver. if (solver == "tromp" ) { //&& notaryid >= 0 ) { // Create solver and initialize it. @@ -1903,12 +2077,12 @@ void static BitcoinMiner() for (int i = 0; i < nThreads; i++) { #ifdef ENABLE_WALLET - if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH || (ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0) ) + if ( ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH ) minerThreads->create_thread(boost::bind(&BitcoinMiner, pwallet)); else minerThreads->create_thread(boost::bind(&BitcoinMiner_noeq, pwallet)); #else - if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH || (ASSETCHAINS_STAKED != 0 && KOMODO_MININGTHREADS == 0) ) + if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH ) minerThreads->create_thread(&BitcoinMiner); else minerThreads->create_thread(&BitcoinMiner_noeq); diff --git a/src/musigtest.py b/src/musigtest.py new file mode 100755 index 000000000..a452b7e97 --- /dev/null +++ b/src/musigtest.py @@ -0,0 +1,200 @@ +#!/usr/bin/env python3 +import platform +import os +import re +import json +import random +import base58 +import binascii +import hashlib +import sys +import time +from slickrpc import Proxy + +# fucntion to define rpc_connection +def def_credentials(chain): + rpcport = ''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check " + coin_config_file) + exit(1) + return (Proxy("http://%s:%s@127.0.0.1:%d" % (rpcuser, rpcpassword, int(rpcport)))) + + +# generate address, validate address, dump private key +def genvaldump(rpc_connection): + # get new address + address = rpc_connection.getnewaddress() + # validate address + validateaddress_result = rpc_connection.validateaddress(address) + pubkey = validateaddress_result['pubkey'] + address = validateaddress_result['address'] + # dump private key for the address + privkey = rpc_connection.dumpprivkey(address) + # function output + output = [pubkey, privkey, address] + return(output) + +CHAIN = 'MUSIG' #sys.argv[1] + +rpc = def_credentials(CHAIN) + +pubkeys = [] +address_info = [] +ret = input('Do you want to generate new pubkeys? ').lower() + +if ret.startswith('y'): + numpks = int(input('Enter number of pubkeys to combine: ')) + if os.path.isfile("list.json"): + print('Already have list.json, move it if you would like to generate a new set.') + sys.exit(0) + while len(address_info) < numpks: + addressinfo = genvaldump(rpc) + address_info.append(addressinfo) + f = open("list.json", "w+") + f.write(json.dumps(address_info)) +else: + if os.path.isfile("list.json"): + with open('list.json') as list: + address_info = json.load(list) + else: + sys.exit('No list.json you need to create new pubkeys!') + +for addressinfo in address_info: + pubkeys.append(addressinfo[0]) + +ret = rpc.setpubkey(pubkeys[0]) +ret = rpc.cclib("combine", "18", str(pubkeys)) +pkhash = str(ret['pkhash']) +combinedpk = str(ret['combined_pk']) +print('Your combined pubkey is: ' + combinedpk) +print('Your pkhash is: ' + pkhash) +amount = float(input('Enter amount to send: ')) +if amount == 0: + sys.exit('Cannot send 0 coins. Exiting.') +tmp = str([combinedpk, amount]) +hex = rpc.cclib("send", "18", tmp)['hex'] +senttxid = rpc.sendrawtransaction(hex) +print('Your senttxid is: ' + senttxid) + +print("Waiting for tx to be confirmed") +while True: + confirmed = int(rpc.gettransaction(senttxid)["confirmations"]) + if not confirmed: + time.sleep(10) + else: + print('SentTX confirmed') + break + +scriptPubKey = rpc.getrawtransaction(senttxid,1)['vout'][1]['scriptPubKey']['hex'] +print('Your scriptPubKey is: ' + scriptPubKey) +tmp = str([senttxid, scriptPubKey]) +msg = rpc.cclib("calcmsg", "18", tmp)['msg'] +print('Your msg is: ' + msg) + +i = 0; +commitments = [] +for pubkey in pubkeys: + ret = rpc.setpubkey(pubkey) + tmp = str([i, len(pubkeys), combinedpk, pkhash, msg, i]) + commitments.append(rpc.cclib("session", "18", tmp)['commitment']) + i = i + 1 + +print("Created commitments sucessfully... Sending to all signers.") + +i = 0 +nonces = [] +for pubkey in pubkeys: + ret = rpc.setpubkey(pubkey) + n = 0 + for commitment in commitments: + tmp = str([pkhash, n, commitment, i]) + ret = rpc.cclib("commit", "18", tmp) + try: + nonces.append(ret['nonce']) + except: + x = 1 + n = n + 1 + i = i + 1 + +print("Created nounce's sucessfully... Sending to all signers.") + +i = 0 +partialsigs = [] +for pubkey in pubkeys: + ret = rpc.setpubkey(pubkey) + n = 0 + for nonce in nonces: + tmp = str([pkhash, n, nonce, i]) + ret = rpc.cclib("nonce", "18", tmp) + try: + partialsigs.append(ret['partialsig']) + except: + x = 1 + n = n + 1 + i = i + 1 + +print("Created partial sigs sucessfully... Sending to all signers.") + +i = 0 +combinedsigs = [] +for pubkey in pubkeys: + ret = rpc.setpubkey(pubkey) + n = 0 + for partialsig in partialsigs: + tmp = str([pkhash, n, partialsig, i]) + ret = rpc.cclib("partialsig", "18", tmp) + try: + combinedsigs.append(ret['combinedsig']) + except: + x = 1 + n = n + 1 + i = i + 1 + +print("Created combined sigs sucessfully... Verifying.") + +tmp = str([msg, combinedpk, combinedsigs[0]]) +ret = rpc.cclib("verify", "18", tmp) + +if ret['result'] != "success": + print(ret) + sys.exit('Could not verify signature.') + +print('Verified... Attempting to send.') + +tmp = str([senttxid, scriptPubKey, combinedsigs[0]]) +ret = rpc.cclib("spend", "18", tmp) + +if ret['result'] != "success": + print(ret) + sys.exit('Could not create spend transaction.') + +try: + ret = rpc.sendrawtransaction(ret['hex']) +except: + sys.exit('Could not send transaction.') + +print('Spent txid: ' + ret) diff --git a/src/net.cpp b/src/net.cpp index d403660b6..83dc0782e 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -78,6 +78,8 @@ namespace { // Global state variables // extern uint16_t ASSETCHAINS_P2PPORT; +extern int8_t is_STAKED(const char *chain_name); +extern char ASSETCHAINS_SYMBOL[65]; bool fDiscover = true; bool fListen = true; @@ -1275,7 +1277,6 @@ void ThreadSocketHandler() } } - void ThreadDNSAddressSeed() { // goal: only query DNS seeds if address need is acute @@ -1390,13 +1391,17 @@ void ThreadOpenConnections() if (GetTime() - nStart > 60) { static bool done = false; if (!done) { - //LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); - LogPrintf("Adding fixed seed nodes.\n"); - addrman.Add(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1")); + // skip DNS seeds for staked chains. + if ( is_STAKED(ASSETCHAINS_SYMBOL) == 0 ) { + //LogPrintf("Adding fixed seed nodes as DNS doesn't seem to be available.\n"); + LogPrintf("Adding fixed seed nodes.\n"); + addrman.Add(convertSeed6(Params().FixedSeeds()), CNetAddr("127.0.0.1")); + } done = true; } } + // // Choose an address to connect to based on most recently seen // @@ -1803,6 +1808,12 @@ void StartNode(boost::thread_group& threadGroup, CScheduler& scheduler) Discover(threadGroup); + // skip DNS seeds for staked chains. + extern int8_t is_STAKED(const char *chain_name); + extern char ASSETCHAINS_SYMBOL[65]; + if ( is_STAKED(ASSETCHAINS_SYMBOL) != 0 ) + SoftSetBoolArg("-dnsseed", false); + // // Start threads // diff --git a/src/net.h b/src/net.h index 3e06e9831..b7975bfec 100644 --- a/src/net.h +++ b/src/net.h @@ -293,6 +293,9 @@ public: bool fNetworkNode; bool fSuccessfullyConnected; bool fDisconnect; + // count blocks seen. + int8_t nBlocksinARow; + int8_t nBlocksinARow2; // We use fRelayTxes for two purposes - // a) it allows us to not relay tx invs before receiving the peer's version message // b) the peer may tell us in its version message that we should not relay tx invs diff --git a/src/netbase.cpp b/src/netbase.cpp index 88439f446..5ad6353e9 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -526,7 +526,8 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe else #endif { - LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); + if ( NetworkErrorString(WSAGetLastError()) != "Network is unreachable (101)") + LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError())); CloseSocket(hSocket); return false; } diff --git a/src/notaries_staked.cpp b/src/notaries_staked.cpp new file mode 100644 index 000000000..0b7c97ceb --- /dev/null +++ b/src/notaries_staked.cpp @@ -0,0 +1,129 @@ + +#include "notaries_staked.h" +#include "crosschain.h" +#include "cc/CCinclude.h" +#include + +extern char NOTARYADDRS[64][64]; +extern std::string NOTARY_ADDRESS,NOTARY_PUBKEY; +extern int32_t STAKED_ERA,IS_STAKED_NOTARY,IS_KOMODO_NOTARY; +extern pthread_mutex_t staked_mutex; +extern uint8_t NOTARY_PUBKEY33[33]; + +int8_t is_STAKED(const char *chain_name) +{ + static int8_t STAKED,doneinit; + if ( chain_name[0] == 0 ) + return(0); + if (doneinit == 1 && ASSETCHAINS_SYMBOL[0] != 0) + return(STAKED); + else STAKED = 0; + if ( (strcmp(chain_name, "LABS") == 0) || (strcmp(chain_name, "LABSTH") == 0) ) + STAKED = 1; // These chains are allowed coin emissions. + else if ( (strncmp(chain_name, "LABS", 4) == 0) ) + STAKED = 2; // These chains have no coin emission, block subsidy is always 0, and comission is 0. Notary pay is allowed. + else if ( (strcmp(chain_name, "CFEK") == 0) || (strncmp(chain_name, "CFEK", 4) == 0) ) + STAKED = 3; // These chains have no speical rules at all. + else if ( (strcmp(chain_name, "TEST") == 0) || (strncmp(chain_name, "TEST", 4) == 0) ) + STAKED = 4; // These chains are for testing consensus to create a chain etc. Not meant to be actually used for anything important. + else if ( (strcmp(chain_name, "THIS_CHAIN_IS_BANNED") == 0) ) + STAKED = 255; // Any chain added to this group is banned, no notarisations are valid, as a consensus rule. Can be used to remove a chain from cluster if needed. + doneinit = 1; + return(STAKED); +}; + +int32_t STAKED_era(int timestamp) +{ + int8_t era = 0; + if (timestamp <= STAKED_NOTARIES_TIMESTAMP[0]) + return(1); + for (int32_t i = 1; i < NUM_STAKED_ERAS; i++) + { + if (timestamp <= STAKED_NOTARIES_TIMESTAMP[i] && timestamp >= (STAKED_NOTARIES_TIMESTAMP[i-1] + STAKED_ERA_GAP)) + return(i+1); + } + // if we are in a gap, return era 0, this allows to invalidate notarizations when in GAP. + return(0); +}; + +int8_t StakedNotaryID(std::string ¬aryname, char *Raddress) { + if ( STAKED_ERA != 0 ) + { + for (int8_t i = 0; i < num_notaries_STAKED[STAKED_ERA-1]; i++) { + if ( strcmp(Raddress,NOTARYADDRS[i]) == 0 ) { + notaryname.assign(notaries_STAKED[STAKED_ERA-1][i][0]); + return(i); + } + } + } + return(-1); +} + +int8_t numStakedNotaries(uint8_t pubkeys[64][33],int8_t era) { + int i; int8_t retval = 0; + static uint8_t staked_pubkeys[NUM_STAKED_ERAS][64][33],didinit[NUM_STAKED_ERAS]; + static char ChainName[65]; + + if ( ChainName[0] == 0 ) + { + if ( ASSETCHAINS_SYMBOL[0] == 0 ) + strcpy(ChainName,"KMD"); + else + strcpy(ChainName,ASSETCHAINS_SYMBOL); + } + + if ( era == 0 ) + { + // era is zero so we need to null out the pubkeys. + memset(pubkeys,0,64 * 33); + printf("%s is a STAKED chain and is in an ERA GAP.\n",ChainName); + return(64); + } + else + { + if ( didinit[era-1] == 0 ) + { + for (i=0; i @@ -17,31 +19,50 @@ NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight) { EvalRef eval; NotarisationsInBlock vNotarisations; + CrosschainAuthority auth_STAKED; + int timestamp = block.nTime; for (unsigned int i = 0; i < block.vtx.size(); i++) { CTransaction tx = block.vtx[i]; - // Special case for TXSCL. Should prob be removed at some point. - bool isTxscl = 0; - { - NotarisationData data; - if (ParseNotarisationOpReturn(tx, data)) - if (IsTXSCL(data.symbol)) - isTxscl = 1; + NotarisationData data; + bool parsed = ParseNotarisationOpReturn(tx, data); + if (!parsed) data = NotarisationData(); + if (strlen(data.symbol) == 0) + continue; + + //printf("Checked notarisation data for %s \n",data.symbol); + int authority = GetSymbolAuthority(data.symbol); + + if (authority == CROSSCHAIN_KOMODO) { + if (!eval->CheckNotaryInputs(tx, nHeight, block.nTime)) + continue; + //printf("Authorised notarisation data for %s \n",data.symbol); + } else if (authority == CROSSCHAIN_STAKED) { + // We need to create auth_STAKED dynamically here based on timestamp + int32_t staked_era = STAKED_era(timestamp); + if (staked_era == 0) { + // this is an ERA GAP, so we will ignore this notarization + continue; + if ( is_STAKED(data.symbol) == 255 ) + // this chain is banned... we will discard its notarisation. + continue; + } else { + // pass era slection off to notaries_staked.cpp file + auth_STAKED = Choose_auth_STAKED(staked_era); + } + if (!CheckTxAuthority(tx, auth_STAKED)) + continue; } - if (isTxscl || eval->CheckNotaryInputs(tx, nHeight, block.nTime)) { - NotarisationData data; - if (ParseNotarisationOpReturn(tx, data)) { - vNotarisations.push_back(std::make_pair(tx.GetHash(), data)); - //printf("Parsed a notarisation for: %s, txid:%s, ccid:%i, momdepth:%i\n", - // data.symbol, tx.GetHash().GetHex().data(), data.ccId, data.MoMDepth); - //if (!data.MoMoM.IsNull()) printf("MoMoM:%s\n", data.MoMoM.GetHex().data()); - } - else - LogPrintf("WARNING: Couldn't parse notarisation for tx: %s at height %i\n", - tx.GetHash().GetHex().data(), nHeight); - } + if (parsed) { + vNotarisations.push_back(std::make_pair(tx.GetHash(), data)); + //printf("Parsed a notarisation for: %s, txid:%s, ccid:%i, momdepth:%i\n", + // data.symbol, tx.GetHash().GetHex().data(), data.ccId, data.MoMDepth); + //if (!data.MoMoM.IsNull()) printf("MoMoM:%s\n", data.MoMoM.GetHex().data()); + } else + LogPrintf("WARNING: Couldn't parse notarisation for tx: %s at height %i\n", + tx.GetHash().GetHex().data(), nHeight); } return vNotarisations; } diff --git a/src/notarystats.py b/src/notarystats.py new file mode 100755 index 000000000..49713b6c3 --- /dev/null +++ b/src/notarystats.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +""" +sudo apt-get install python3-dev +sudo apt-get install python3 libgnutls28-dev libssl-dev +sudo apt-get install python3-pip +pip3 install setuptools +pip3 install wheel +pip3 install base58 slick-bitcoinrpc +./notarystats.py +------------------------------------------------ +""" +import platform +import os +import re +import sys +import time +import pprint +from slickrpc import Proxy + +# fucntion to define rpc_connection +def def_credentials(chain): + rpcport = ''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check " + coin_config_file) + exit(1) + return (Proxy("http://%s:%s@127.0.0.1:%d" % (rpcuser, rpcpassword, int(rpcport)))) + + +rpc = def_credentials('KMD') +pp = pprint.PrettyPrinter(indent=2) + +notarynames = [ "0dev1_jl777", "0dev2_kolo", "0dev3_kolo", "0dev4_decker_AR", "a-team_SH", "artik_AR", "artik_EU", "artik_NA", "artik_SH", "badass_EU", "badass_NA", "batman_AR", "batman_SH", "ca333", "chainmakers_EU", "chainmakers_NA", "chainstrike_SH", "cipi_AR", "cipi_NA", "crackers_EU", "crackers_NA", "dwy_EU", "emmanux_SH", "etszombi_EU", "fullmoon_AR", "fullmoon_NA", "fullmoon_SH", "goldenman_EU", "indenodes_AR", "indenodes_EU", "indenodes_NA", "indenodes_SH", "jackson_AR", "jeezy_EU", "karasugoi_NA", "komodoninja_EU", "komodoninja_SH", "komodopioneers_SH", "libscott_SH", "lukechilds_AR", "madmax_AR", "meshbits_AR", "meshbits_SH", "metaphilibert_AR", "metaphilibert_SH", "patchkez_SH", "pbca26_NA", "peer2cloud_AR", "peer2cloud_SH", "polycryptoblog_NA", "hyper_AR", "hyper_EU", "hyper_SH", "hyper_NA", "popcornbag_AR", "popcornbag_NA", "alien_AR", "alien_EU", "thegaltmines_NA", "titomane_AR", "titomane_EU", "titomane_SH", "webworker01_NA", "xrobesx_NA" ] +notaries = 64 * [0] + +startheight = 821657 #Second time filter for assetchains (block 821657) for KMD its 814000 +stopheight = 1307200 +for i in range(startheight,stopheight): + ret = rpc.getNotarisationsForBlock(i) + KMD = ret['KMD'] + if len(KMD) > 0: + for obj in KMD: + #for now skip KMD for this. As official stats are from BTC chain + # this can be reversed to !== to count KMD numbers :) + if obj['chain'] == 'KMD': + continue; + for notary in obj['notaries']: + notaries[notary] = notaries[notary] + 1 + +i = 0 +SH = [] +AR = [] +EU = [] +NA = [] +for notary in notaries: + tmpnotary = {} + tmpnotary['node'] = notarynames[i] + tmpnotary['ac_count'] = notary + if notarynames[i].endswith('SH'): + SH.append(tmpnotary) + elif notarynames[i].endswith('AR'): + AR.append(tmpnotary) + elif notarynames[i].endswith('EU'): + EU.append(tmpnotary) + elif notarynames[i].endswith('NA'): + NA.append(tmpnotary) + i = i + 1 + +regions = {} +regions['SH'] = sorted(SH, key=lambda k: k['ac_count'], reverse=True) +regions['AR'] = sorted(AR, key=lambda k: k['ac_count'], reverse=True) +regions['EU'] = sorted(EU, key=lambda k: k['ac_count'], reverse=True) +regions["NA"] = sorted(NA, key=lambda k: k['ac_count'], reverse=True) + +pp.pprint(regions) diff --git a/src/pow.cpp b/src/pow.cpp index cc2b0b7c2..1716099ee 100644 --- a/src/pow.cpp +++ b/src/pow.cpp @@ -44,10 +44,15 @@ unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const unsigned int GetNextWorkRequired(const CBlockIndex* pindexLast, const CBlockHeader *pblock, const Consensus::Params& params) { - if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH) + if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH && ASSETCHAINS_STAKED == 0) return lwmaGetNextWorkRequired(pindexLast, pblock, params); - unsigned int nProofOfWorkLimit = UintToArith256(params.powLimit).GetCompact(); + arith_uint256 bnLimit; + if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH) + bnLimit = UintToArith256(params.powLimit); + else + bnLimit = UintToArith256(params.powAlternate); + unsigned int nProofOfWorkLimit = bnLimit.GetCompact(); // Genesis block if (pindexLast == NULL ) return nProofOfWorkLimit; @@ -102,7 +107,13 @@ unsigned int CalculateNextWorkRequired(arith_uint256 bnAvg, nActualTimespan = params.MaxActualTimespan(); // Retarget - const arith_uint256 bnPowLimit = UintToArith256(params.powLimit); + arith_uint256 bnLimit; + if (ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH) + bnLimit = UintToArith256(params.powLimit); + else + bnLimit = UintToArith256(params.powAlternate); + + const arith_uint256 bnPowLimit = bnLimit; //UintToArith256(params.powLimit); arith_uint256 bnNew {bnAvg}; bnNew /= params.AveragingWindowTimespan(); bnNew *= nActualTimespan; @@ -133,6 +144,8 @@ unsigned int lwmaCalculateNextWorkRequired(const CBlockIndex* pindexLast, const bnLimit = UintToArith256(params.powAlternate); unsigned int nProofOfWorkLimit = bnLimit.GetCompact(); + + //printf("PoWLimit: %u\n", nProofOfWorkLimit); // Find the first block in the averaging interval as we total the linearly weighted average const CBlockIndex* pindexFirst = pindexLast; diff --git a/src/pow/tromp/equi.h b/src/pow/tromp/equi.h index f6d8803c2..b474194fc 100644 --- a/src/pow/tromp/equi.h +++ b/src/pow/tromp/equi.h @@ -75,8 +75,15 @@ int verifyrec(const crypto_generichash_blake2b_state *ctx, u32 *indices, uchar * } int compu32(const void *pa, const void *pb) { + int32_t retval; u32 a = *(u32 *)pa, b = *(u32 *)pb; - return anBits))); result.push_back(Pair("difficulty", GetDifficulty(blockindex))); result.push_back(Pair("chainwork", blockindex->chainPower.chainWork.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); + result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); if (blockindex->pprev) result.push_back(Pair("previousblockhash", blockindex->pprev->GetBlockHash().GetHex())); @@ -174,7 +173,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); + result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); UniValue deltas(UniValue::VARR); @@ -292,7 +291,7 @@ UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool tx result.push_back(Pair("height", blockindex->GetHeight())); result.push_back(Pair("version", block.nVersion)); result.push_back(Pair("merkleroot", block.hashMerkleRoot.GetHex())); - result.push_back(Pair("segid", (int64_t)blockindex->segid)); + result.push_back(Pair("segid", (int)komodo_segid(0,blockindex->GetHeight()))); result.push_back(Pair("finalsaplingroot", block.hashFinalSaplingRoot.GetHex())); UniValue txs(UniValue::VARR); BOOST_FOREACH(const CTransaction&tx, block.vtx) @@ -380,10 +379,11 @@ UniValue getdifficulty(const UniValue& params, bool fHelp) return GetNetworkDifficulty(); } -bool myIsutxo_spentinmempool(uint256 txid,int32_t vout) +bool myIsutxo_spentinmempool(uint256 &spenttxid,int32_t &spentvini,uint256 txid,int32_t vout) { //char *uint256_str(char *str,uint256); char str[65]; //LOCK(mempool.cs); + int32_t vini = 0; BOOST_FOREACH(const CTxMemPoolEntry &e,mempool.mapTx) { const CTransaction &tx = e.GetTx(); @@ -392,7 +392,12 @@ bool myIsutxo_spentinmempool(uint256 txid,int32_t vout) { //fprintf(stderr,"%s/v%d ",uint256_str(str,txin.prevout.hash),txin.prevout.n); if ( txin.prevout.n == vout && txin.prevout.hash == txid ) + { + spenttxid = hash; + spentvini = vini; return(true); + } + vini++; } //fprintf(stderr,"are vins for %s\n",uint256_str(str,hash)); } @@ -621,8 +626,6 @@ UniValue getblockhash(const UniValue& params, bool fHelp) return pblockindex->GetBlockHash().GetHex(); } -extern uint64_t ASSETCHAINS_STAKED; - UniValue getlastsegidstakes(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -647,22 +650,39 @@ UniValue getlastsegidstakes(const UniValue& params, bool fHelp) LOCK(cs_main); int depth = params[0].get_int(); + if ( depth > chainActive.Height() ) + throw runtime_error("Not enough blocks to scan back that far.\n"); + int32_t segids[64] = {0}; + int32_t pow = 0; + int32_t notset = 0; for (int64_t i = chainActive.Height(); i > chainActive.Height()-depth; i--) { - CBlockIndex* pblockindex = chainActive[i]; - if ( pblockindex->segid >= 0 ) - segids[pblockindex->segid] += 1; + int8_t segid = komodo_segid(0,i); + //CBlockIndex* pblockindex = chainActive[i]; + if ( segid >= 0 ) + segids[segid] += 1; + else if ( segid == -1 ) + pow++; + else + notset++; } - + + int8_t posperc = 100*(depth-pow)/depth; + UniValue ret(UniValue::VOBJ); + UniValue objsegids(UniValue::VOBJ); for (int8_t i = 0; i < 64; i++) { char str[4]; sprintf(str, "%d", i); - ret.push_back(Pair(str,segids[i])); + objsegids.push_back(Pair(str,segids[i])); } + ret.push_back(Pair("NotSet",notset)); + ret.push_back(Pair("PoW",pow)); + ret.push_back(Pair("PoSPerc",posperc)); + ret.push_back(Pair("SegIds",objsegids)); return ret; } @@ -891,20 +911,6 @@ UniValue gettxoutsetinfo(const UniValue& params, bool fHelp) return ret; } -#include "komodo_defs.h" -#include "komodo_structs.h" - -#define IGUANA_MAXSCRIPTSIZE 10001 -#define KOMODO_KVDURATION 1440 -#define KOMODO_KVBINARY 2 -extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; -extern int32_t ASSETCHAINS_LWMAPOS; -uint64_t komodo_paxprice(uint64_t *seedp,int32_t height,char *base,char *rel,uint64_t basevolume); -int32_t komodo_paxprices(int32_t *heights,uint64_t *prices,int32_t max,char *base,char *rel); -int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); -char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160,int32_t len); -int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width); -int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); UniValue kvsearch(const UniValue& params, bool fHelp) { @@ -1150,43 +1156,148 @@ UniValue paxprice(const UniValue& params, bool fHelp) return ret; } -UniValue paxprices(const UniValue& params, bool fHelp) +int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) { - if ( fHelp || params.size() != 3 ) - throw runtime_error("paxprices \"base\" \"rel\" maxsamples\n"); + int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,ignore,rngval; uint32_t rawprices[1440*6],*ptr; int64_t *tmpbuf; + width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; + komodo_heightpricebits(&seed,rawprices,firstheight + numblocks - 1); + if ( firstheight < width ) + return(-1); + for (i=0; i sizeof(heights)/sizeof(*heights) ) - maxsamples = sizeof(heights)/sizeof(*heights); - ret.push_back(Pair("base", base)); - ret.push_back(Pair("rel", rel)); - n = komodo_paxprices(heights,prices,maxsamples,(char *)base.c_str(),(char *)rel.c_str()); + nextheight = komodo_nextheight(); UniValue a(UniValue::VARR); - for (i=0; i2; i++,ht--) { - UniValue item(UniValue::VOBJ); - if ( heights[i] < 0 || heights[i] > chainActive.Height() ) + if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); else { - CBlockIndex *pblockindex = chainActive[heights[i]]; - - item.push_back(Pair("t", (int64_t)pblockindex->nTime)); - item.push_back(Pair("p", (double)prices[i] / COIN)); - a.push_back(item); + if ( (n= komodo_heightpricebits(0,rawprices,ht)) > 0 ) + { + if ( n != numpricefeeds ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); + else + { + for (j=0; j= width ) + { + for (i=0; i 3) @@ -1631,6 +1742,87 @@ UniValue getmempoolinfo(const UniValue& params, bool fHelp) return mempoolInfoToJSON(); } +inline CBlockIndex* LookupBlockIndex(const uint256& hash) +{ + AssertLockHeld(cs_main); + BlockMap::const_iterator it = mapBlockIndex.find(hash); + return it == mapBlockIndex.end() ? nullptr : it->second; +} + +UniValue getchaintxstats(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 2) + throw runtime_error( + "getchaintxstats\n" + "\nCompute statistics about the total number and rate of transactions in the chain.\n" + "\nArguments:\n" + "1. nblocks (numeric, optional) Number of blocks in averaging window.\n" + "2. blockhash (string, optional) The hash of the block which ends the window.\n" + "\nResult:\n" + "{\n" + " \"time\": xxxxx, (numeric) The timestamp for the final block in the window in UNIX format.\n" + " \"txcount\": xxxxx, (numeric) The total number of transactions in the chain up to that point.\n" + " \"window_final_block_hash\": \"...\", (string) The hash of the final block in the window.\n" + " \"window_block_count\": xxxxx, (numeric) Size of the window in number of blocks.\n" + " \"window_tx_count\": xxxxx, (numeric) The number of transactions in the window. Only returned if \"window_block_count\" is > 0.\n" + " \"window_interval\": xxxxx, (numeric) The elapsed time in the window in seconds. Only returned if \"window_block_count\" is > 0.\n" + " \"txrate\": x.xx, (numeric) The average rate of transactions per second in the window. Only returned if \"window_interval\" is > 0.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getchaintxstats", "") + + HelpExampleRpc("getchaintxstats", "2016") + ); + + const CBlockIndex* pindex; + int blockcount = 30 * 24 * 60 * 60 / Params().GetConsensus().nPowTargetSpacing; // By default: 1 month + + if (params[1].isNull()) { + LOCK(cs_main); + pindex = chainActive.Tip(); + } else { + uint256 hash(ParseHashV(params[1], "blockhash")); + LOCK(cs_main); + pindex = LookupBlockIndex(hash); + if (!pindex) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + } + if (!chainActive.Contains(pindex)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block is not in main chain"); + } + } + + assert(pindex != nullptr); + + if (params[0].isNull()) { + blockcount = std::max(0, std::min(blockcount, pindex->GetHeight() - 1)); + } else { + blockcount = params[0].get_int(); + + if (blockcount < 0 || (blockcount > 0 && blockcount >= pindex->GetHeight())) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block count: should be between 0 and the block's height - 1"); + } + } + + const CBlockIndex* pindexPast = pindex->GetAncestor(pindex->GetHeight() - blockcount); + int nTimeDiff = pindex->GetMedianTimePast() - pindexPast->GetMedianTimePast(); + int nTxDiff = pindex->nChainTx - pindexPast->nChainTx; + + UniValue ret(UniValue::VOBJ); + ret.pushKV("time", (int64_t)pindex->nTime); + ret.pushKV("txcount", (int64_t)pindex->nChainTx); + ret.pushKV("window_final_block_hash", pindex->GetBlockHash().GetHex()); + ret.pushKV("window_block_count", blockcount); + if (blockcount > 0) { + ret.pushKV("window_tx_count", nTxDiff); + ret.pushKV("window_interval", nTimeDiff); + if (nTimeDiff > 0) { + ret.pushKV("txrate", ((double)nTxDiff) / nTimeDiff); + } + } + + return ret; +} + UniValue invalidateblock(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -1659,7 +1851,7 @@ UniValue invalidateblock(const UniValue& params, bool fHelp) } if (state.IsValid()) { - ActivateBestChain(state); + ActivateBestChain(true,state); } if (!state.IsValid()) { @@ -1698,7 +1890,7 @@ UniValue reconsiderblock(const UniValue& params, bool fHelp) } if (state.IsValid()) { - ActivateBestChain(state); + ActivateBestChain(true,state); } if (!state.IsValid()) { @@ -1718,6 +1910,7 @@ static const CRPCCommand commands[] = { "blockchain", "getblockhash", &getblockhash, true }, { "blockchain", "getblockheader", &getblockheader, true }, { "blockchain", "getchaintips", &getchaintips, true }, + { "blockchain", "getchaintxstats", &getchaintxstats, true }, { "blockchain", "getdifficulty", &getdifficulty, true }, { "blockchain", "getmempoolinfo", &getmempoolinfo, true }, { "blockchain", "getrawmempool", &getrawmempool, true }, diff --git a/src/rpc/client.cpp b/src/rpc/client.cpp index ac45cd107..cab89092c 100644 --- a/src/rpc/client.cpp +++ b/src/rpc/client.cpp @@ -51,6 +51,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "sendtoaddress", 1 }, { "sendtoaddress", 4 }, { "settxfee", 0 }, + { "getnotarysendmany", 0 }, + { "getnotarysendmany", 1 }, { "getreceivedbyaddress", 1 }, { "getreceivedbyaccount", 1 }, { "listreceivedbyaddress", 0 }, @@ -87,6 +89,8 @@ static const CRPCConvertParam vRPCConvertParams[] = { "listunspent", 2 }, { "getblock", 1 }, { "getblockheader", 1 }, + { "getchaintxstats", 0 }, + { "getlastsegidstakes", 0 }, { "gettransaction", 1 }, { "getrawtransaction", 1 }, { "getlastsegidstakes", 0 }, @@ -167,6 +171,7 @@ static const CRPCConvertParam vRPCConvertParams[] = { "assetchainproof", 1}, { "crosschainproof", 1}, { "getproofroot", 2}, + { "getNotarisationsForBlock", 0}, { "height_MoM", 1}, { "calc_MoM", 2}, }; diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index a0a3b507f..0feb5173c 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -24,6 +24,7 @@ #include "consensus/validation.h" #include "cc/eval.h" #include "cc/utils.h" +#include "cc/CCinclude.h" #include "main.h" #include "primitives/transaction.h" #include "rpc/server.h" @@ -33,22 +34,35 @@ #include "script/script_error.h" #include "script/sign.h" #include "script/standard.h" +#include "notaries_staked.h" + +#include "key_io.h" +#include "cc/CCImportGateway.h" #include #include #include - using namespace std; +#define RETURN_IF_ERROR(CCerror) if ( CCerror != "" ) { ERR_RESULT(CCerror); return(result); } +#define ERR_RESULT(x) result.push_back(Pair("result", "error")) , result.push_back(Pair("error", x)); + +extern std::string CCerror; +extern std::string ASSETCHAINS_SELFIMPORT; +extern uint16_t ASSETCHAINS_CODAPORT, ASSETCHAINS_BEAMPORT; +int32_t ensure_CCrequirements(uint8_t evalcode); + int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip); int32_t komodo_MoMoMdata(char *hexstr,int32_t hexsize,struct komodo_ccdataMoMoM *mdata,char *symbol,int32_t kmdheight,int32_t notarized_height); struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi); uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); +int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); extern std::string ASSETCHAINS_SELFIMPORT; -uint256 Parseuint256(char *hexstr); -int32_t GetSelfimportProof(std::string source,CMutableTransaction &mtx,CScript &scriptPubKey,TxProof &proof,uint64_t burnAmount,std::vector rawtx,uint256 txid,std::vector rawproof); +std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx); +int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount); +std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector vouts); UniValue assetchainproof(const UniValue& params, bool fHelp) { @@ -110,7 +124,7 @@ UniValue height_MoM(const UniValue& params, bool fHelp) ret.push_back(Pair("kmdendi",kmdendi)); } } else ret.push_back(Pair("error",(char *)"no MoM for height")); - + return ret; } @@ -163,10 +177,10 @@ UniValue calc_MoM(const UniValue& params, bool fHelp) UniValue migrate_converttoexport(const UniValue& params, bool fHelp) { - std::vector rawproof; uint8_t *ptr; int32_t i; uint32_t ccid = ASSETCHAINS_CC; - if (fHelp || params.size() != 3) + std::vector rawproof; uint8_t *ptr; uint8_t i; uint32_t ccid = ASSETCHAINS_CC; uint64_t txfee = 10000; + if (fHelp || params.size() != 2) throw runtime_error( - "migrate_converttoexport rawTx dest_symbol export_amount\n" + "migrate_converttoexport rawTx dest_symbol\n" "\nConvert a raw transaction to a cross-chain export.\n" "If neccesary, the transaction should be funded using fundrawtransaction.\n" "Finally, the transaction should be signed using signrawtransaction\n" @@ -190,22 +204,22 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp) if (targetSymbol.size() == 0 || targetSymbol.size() > 32) throw runtime_error("targetSymbol length must be >0 and <=32"); - CAmount burnAmount = AmountFromValue(params[2]); + if (strcmp(ASSETCHAINS_SYMBOL,targetSymbol.c_str()) == 0) + throw runtime_error("cant send a coin to the same chain"); + + CAmount burnAmount = 0; + + for (int i=0; i 0 ) - // throw runtime_error("self-import chains cant be fungible"); + throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export a negative or zero value."); + if (burnAmount > 1000000LL*COIN) + throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export more than 1 million coins per export."); + rawproof.resize(strlen(ASSETCHAINS_SYMBOL)); ptr = rawproof.data(); for (i=0; i> burnTx)) throw runtime_error("Couldn't parse burnTx"); - - + + vector payouts; if (!E_UNMARSHAL(ParseHexV(params[1], "argument 2"), ss >> payouts)) throw runtime_error("Couldn't parse payouts"); @@ -256,6 +270,7 @@ UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp) TxProof proof = GetAssetchainProof(burnTx.GetHash(),burnTx); CTransaction importTx = MakeImportCoinTransaction(proof, burnTx, payouts); + return HexStr(E_MARSHAL(ss << importTx)); } @@ -266,7 +281,7 @@ UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp) throw runtime_error("migrate_completeimporttransaction importTx\n\n" "Takes a cross chain import tx with proof generated on assetchain " "and extends proof to target chain proof root"); - + if (ASSETCHAINS_SYMBOL[0] != 0) throw runtime_error("Must be called on KMD"); @@ -281,34 +296,515 @@ UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp) UniValue selfimport(const UniValue& params, bool fHelp) { - CMutableTransaction mtx; - std::string source; TxProof proof; CTransaction burnTx,tx; CTxOut burnOut; uint64_t burnAmount; uint256 txid,blockHash; std::vector vouts; std::vector rawtx,rawproof; CScript scriptPubKey; + UniValue result(UniValue::VOBJ); + CMutableTransaction sourceMtx, templateMtx; + std::string destaddr; + std::string source; + std::string rawsourcetx; + CTransaction burnTx; + CTxOut burnOut; + uint64_t burnAmount; + uint256 sourcetxid, blockHash; + std::vector vouts; + std::vector rawproof, rawproofEmpty; + int32_t ivout = 0; + CScript scriptPubKey; + TxProof proof; + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) throw runtime_error("selfimport only works on -ac_import chains"); - if (fHelp || params.size() < 3 || params.size() > 5 ) - throw runtime_error("selfimport rawtx txid burnamount [rawproof source]\n\n" - "creates signed selfimport transaction"); - rawtx = ParseHex(params[0].get_str().c_str()); - txid = Parseuint256((char *)params[1].get_str().c_str()); // allow for txid != hash(rawtx) - burnAmount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - source = ASSETCHAINS_SELFIMPORT; - if ( params.size() >= 4 ) + + if (fHelp || params.size() != 2) + throw runtime_error("selfimport destaddr amount\n" + //old: "selfimport rawsourcetx sourcetxid {nvout|\"find\"} amount \n" + //TODO: "or selfimport rawburntx burntxid {nvout|\"find\"} rawproof source bindtxid height} \n" + "\ncreates self import coin transaction"); + +/* OLD selfimport schema: + rawsourcetx = params[0].get_str(); + sourcetxid = Parseuint256((char *)params[1].get_str().c_str()); // allow for txid != hash(rawtx) + + int32_t ivout = -1; + if( params[2].get_str() != "find" ) { + if( !std::all_of(params[2].get_str().begin(), params[2].get_str().end(), ::isdigit) ) // check if not all chars are digit + throw std::runtime_error("incorrect nvout param"); + + ivout = atoi(params[2].get_str().c_str()); + } + + burnAmount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; */ + + destaddr = params[0].get_str(); + burnAmount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; + + source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param + /* TODO for gateways: + if ( params.size() >= 5 ) { - rawproof = ParseHex(params[3].get_str().c_str()); - if ( params.size() == 5 ) - source = params[4].get_str(); + rawproof = ParseHex(params[4].get_str().c_str()); + if ( params.size() == 6 ) + source = params[5].get_str(); + } */ + + if (source == "BEAM") + { + if (ASSETCHAINS_BEAMPORT == 0) + return(-1); + // confirm via ASSETCHAINS_BEAMPORT that burnTx/hash is a valid BEAM burn + // return(0); + return -1; } - if ( GetSelfimportProof(source,mtx,scriptPubKey,proof,burnAmount,rawtx,txid,rawproof) < 0 ) - throw std::runtime_error("Failed validating selfimport"); - vouts = mtx.vout; - burnOut = MakeBurnOutput(burnAmount,0xffffffff,ASSETCHAINS_SELFIMPORT,vouts,rawproof); - mtx.vout.clear(); - mtx.vout.push_back(burnOut); - burnTx = mtx; - return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(proof,burnTx,vouts))); + else if (source == "CODA") + { + if (ASSETCHAINS_CODAPORT == 0) + return(-1); + // confirm via ASSETCHAINS_CODAPORT that burnTx/hash is a valid CODA burn + // return(0); + return -1; + } + else if (source == "PUBKEY") + { + + CTxDestination dest = DecodeDestination(destaddr.c_str()); + rawsourcetx = MakeSelfImportSourceTx(dest, burnAmount, sourceMtx); + sourcetxid = sourceMtx.GetHash(); + + // prepare self-import 'quasi-burn' tx and also create vout for import tx (in mtx.vout): + if (GetSelfimportProof(source, templateMtx, scriptPubKey, proof, rawsourcetx, ivout, sourcetxid, burnAmount) < 0) + throw std::runtime_error("Failed validating selfimport"); + + vouts = templateMtx.vout; + burnOut = MakeBurnOutput(burnAmount, 0xffffffff, ASSETCHAINS_SELFIMPORT, vouts, rawproofEmpty); + templateMtx.vout.clear(); + templateMtx.vout.push_back(burnOut); // burn tx has only opret with vouts and optional proof + + burnTx = templateMtx; // complete the creation of 'quasi-burn' tx + + std::string hextx = HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(proof, burnTx, vouts))); + + CTxDestination address; + bool fValidAddress = ExtractDestination(scriptPubKey, address); + + result.push_back(Pair("sourceTxHex", rawsourcetx)); + result.push_back(Pair("importTxHex", hextx)); + result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx + result.push_back(Pair("DestinationAddress", EncodeDestination(address))); // notify user about the address where the funds will be sent + + return result; + } + else if (source == ASSETCHAINS_SELFIMPORT) + { + throw std::runtime_error("not implemented yet\n"); + + if (params.size() != 8) + throw runtime_error("use \'selfimport rawburntx burntxid nvout rawproof source bindtxid height\' to import from a coin chain\n"); + + uint256 bindtxid = Parseuint256((char *)params[6].get_str().c_str()); + int32_t height = atoi((char *)params[7].get_str().c_str()); + + + // source is external coin is the assetchains symbol in the burnTx OP_RETURN + // burnAmount, rawtx and rawproof should be enough for gatewaysdeposit equivalent + //std::string hextx = MakeGatewaysImportTx(0, bindtxid, height, source, rawproof, rawsourcetx, ivout, ""); + + // result.push_back(Pair("hex", hextx)); + // result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx + } + return result; +} + +bool GetNotarisationNotaries(uint8_t notarypubkeys[64][33], int8_t &numNN, const std::vector &vin, std::vector &NotarisationNotaries); + + +UniValue importdual(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; + std::string hex,source,sourceaddr,destaddr,burntxid; uint64_t burnAmount; + CPubKey destpub; std::vector vouts; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importdual only works on -ac_import chains"); + + if (fHelp || params.size() < 4) + throw runtime_error("burntxid source_addr dest_pubkey amount\n"); + + CCerror = ""; + + burntxid = params[0].get_str(); + sourceaddr = params[1].get_str(); + destaddr = params[2].get_str(); + burnAmount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; + + source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param + + CTxDestination dest = DecodeDestination(destaddr.c_str()); + CScript scriptPubKey = GetScriptForDestination(dest); + vouts.push_back(CTxOut(burnAmount,scriptPubKey)); + + if (source == "BEAM") + { + if (ASSETCHAINS_BEAMPORT == 0) + return(-1); + // confirm via ASSETCHAINS_BEAMPORT that burnTx/hash is a valid BEAM burn + // return(0); + return -1; + } + else if (source == "CODA") + { + if (ASSETCHAINS_CODAPORT == 0) + return(-1); + hex=MakeCodaImportTx(0,burntxid,sourceaddr,vouts); + // confirm via ASSETCHAINS_CODAPORT that burnTx/hash is a valid CODA burn + // return(0); + } + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importdual"); + return result; +} + +UniValue importgatewayinfo(const UniValue& params, bool fHelp) +{ + uint256 txid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaybind only works on -ac_import chains"); + if ( fHelp || params.size() != 1 ) + throw runtime_error("importgatewayinfo bindtxid\n"); + txid = Parseuint256(params[0].get_str().c_str()); + return(ImportGatewayInfo(txid)); +} + +UniValue importgatewaybind(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; std::vector pubkey; + std::string hex,coin; int32_t i,M,N; std::vector pubkeys; + uint256 oracletxid; uint8_t p1,p2,p3,p4; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaybind only works on -ac_import chains"); + if ( fHelp || params.size() != 8) + throw runtime_error("use \'importgatewaybind coin orcletxid M N pubkeys pubtype p2shtype wiftype [taddr]\' to bind an import gateway\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + CCerror = ""; + coin = params[0].get_str(); + oracletxid = Parseuint256(params[1].get_str().c_str()); + M = atoi(params[2].get_str().c_str()); + N = atoi(params[3].get_str().c_str()); + if ( M > N || N == 0 || N > 15 ) + throw runtime_error("illegal M or N > 15\n"); + if ( params.size() < 4+N+3 ) + throw runtime_error("not enough parameters for N pubkeys\n"); + for (i=0; i 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaybind"); + return result; +} + +UniValue importgatewaydeposit(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; std::vector rawproof; + std::string hex,coin,rawburntx; int32_t height,burnvout; + CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaydeposit only works on -ac_import chains"); + if ( fHelp || params.size() != 8) + throw runtime_error("use \'importgatewaydeposit bindtxid height coin burntxid nvout rawburntx rawproof destpub\' to import deposited coins\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + CCerror = ""; + bindtxid = Parseuint256(params[0].get_str().c_str()); + height = atoi(params[1].get_str().c_str()); + coin = params[2].get_str(); + burntxid = Parseuint256(params[3].get_str().c_str()); + burnvout = atoi(params[4].get_str().c_str()); + rawburntx = params[5].get_str(); + rawproof = ParseHex(params[6].get_str()); + destpub = ParseHex(params[7].get_str()); + if (coin == "BEAM" || coin == "CODA") + { + ERR_RESULT("for BEAM and CODA import use importdual RPC"); + return result; + } + else if (coin != ASSETCHAINS_SELFIMPORT) + { + ERR_RESULT("source coin not equal to ac_import name"); + return result; + } + hex = ImportGatewayDeposit(0, bindtxid, height, coin, burntxid, burnvout, rawburntx, rawproof, destpub); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaydeposit"); + return result; +} + +UniValue importgatewaywithdraw(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + CMutableTransaction mtx; std::vector rawproof; + std::string hex,coin,rawburntx; int64_t amount; int32_t height,burnvout; + CPubKey destpub; std::vector vouts; uint256 bindtxid,burntxid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaywithdraw only works on -ac_import chains"); + if ( fHelp || params.size() != 4) + throw runtime_error("use \'importgatewaywithdraw bindtxid coin withdrawpub amount\' to burn imported coins and withdraw them on external chain\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + CCerror = ""; + bindtxid = Parseuint256(params[0].get_str().c_str()); + coin = params[1].get_str(); + destpub = ParseHex(params[2].get_str()); + amount = atof((char *)params[3].get_str().c_str()) * COIN + 0.00000000499999; + if (coin == "BEAM" || coin == "CODA") + { + ERR_RESULT("for BEAM and CODA import use importdual RPC"); + return result; + } + else if (coin != ASSETCHAINS_SELFIMPORT) + { + ERR_RESULT("source coin not equal to ac_import name"); + return result; + } + hex = ImportGatewayWithdraw(0, bindtxid, coin, destpub, amount); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaywithdraw"); + return result; +} + +UniValue importgatewaypartialsign(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); std::string coin,parthex,hex; uint256 txid; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewayspartialsign only works on -ac_import chains"); + if ( fHelp || params.size() != 3 ) + throw runtime_error("importgatewayspartialsign txidaddr refcoin hex\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + txid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + parthex = params[2].get_str(); + hex = ImportGatewayPartialSign(0,txid,coin,parthex); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex",hex)); + } else ERR_RESULT("couldnt importgatewayspartialsign"); + return(result); +} + +UniValue importgatewaycompletesigning(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); uint256 withdrawtxid; std::string txhex,hex,coin; + + if ( ASSETCHAINS_SELFIMPORT.size() == 0 ) + throw runtime_error("importgatewaycompletesigning only works on -ac_import chains"); + if ( fHelp || params.size() != 3 ) + throw runtime_error("importgatewaycompletesigning withdrawtxid coin hex\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + withdrawtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + txhex = params[2].get_str(); + hex = ImportGatewayCompleteSigning(0,withdrawtxid,coin,txhex); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaycompletesigning"); + return(result); +} + +UniValue importgatewaymarkdone(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); uint256 completetxid; std::string hex,coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewaymarkdone completesigningtx coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + completetxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + hex = ImportGatewayMarkDone(0,completetxid,coin); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt importgatewaymarkdone"); + return(result); +} + +UniValue importgatewaypendingdeposits(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; std::string coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewaypendingdeposits bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(ImportGatewayPendingDeposits(bindtxid,coin)); +} + +UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; std::string coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewaypendingwithdraws bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(ImportGatewayPendingWithdraws(bindtxid,coin)); +} + +UniValue importgatewayprocessed(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; std::string coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("importgatewayprocessed bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(ImportGatewayProcessedWithdraws(bindtxid,coin)); +} + +UniValue importgatewayexternaladdress(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; CPubKey pubkey; + + if ( fHelp || params.size() != 2) + throw runtime_error("importgatewayexternaladdress bindtxid pubkey\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + pubkey = ParseHex(params[1].get_str().c_str()); + return(ImportGatewayExternalAddress(bindtxid,pubkey)); +} + +UniValue importgatewaydumpprivkey(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; + + if ( fHelp || params.size() != 2) + throw runtime_error("importgatewaydumpprivkey bindtxid address\n"); + if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + std::string strAddress = params[1].get_str(); + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid transparent address"); + } + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + } + CKey vchSecret; + // if (!pwalletMain->GetKey(*keyID, vchSecret)) { + // throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + //} + return(ImportGatewayDumpPrivKey(bindtxid,vchSecret)); } UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) +{ + // TODO take timestamp as param, and loop blockindex to get starting/finish height. + if (fHelp || params.size() != 1) + throw runtime_error("getNotarisationsForBlock height\n\n" + "Takes a block height and returns notarisation information " + "within the block"); + + LOCK(cs_main); + int32_t height = params[0].get_int(); + if ( height < 0 || height > chainActive.Height() ) + throw runtime_error("height out of range.\n"); + + uint256 blockHash = chainActive[height]->GetBlockHash(); + + NotarisationsInBlock nibs; + GetBlockNotarisations(blockHash, nibs); + UniValue out(UniValue::VOBJ); + //out.push_back(make_pair("blocktime",(int))); + UniValue labs(UniValue::VARR); + UniValue kmd(UniValue::VARR); + // Gets KMD notaries on KMD... but LABS notaries on labs chains needs to be fixed so LABS are identified on KMD. + int8_t numNN = 0; uint8_t notarypubkeys[64][33] = {0}; + numNN = komodo_notaries(notarypubkeys, height, chainActive[height]->nTime); + + BOOST_FOREACH(const Notarisation& n, nibs) + { + UniValue item(UniValue::VOBJ); UniValue notaryarr(UniValue::VARR); std::vector NotarisationNotaries; + if ( is_STAKED(n.second.symbol) != 0 ) + continue; // for now just skip this... need to fetch diff pubkeys for these chains. labs.push_back(item); + uint256 hash; CTransaction tx; + if ( GetTransaction(n.first,tx,hash,false) ) + { + if ( !GetNotarisationNotaries(notarypubkeys, numNN, tx.vin, NotarisationNotaries) ) + continue; + if ( NotarisationNotaries.size() < numNN/5 ) + continue; + } + item.push_back(make_pair("txid", n.first.GetHex())); + item.push_back(make_pair("chain", n.second.symbol)); + item.push_back(make_pair("height", (int)n.second.height)); + item.push_back(make_pair("blockhash", n.second.blockHash.GetHex())); + item.push_back(make_pair("KMD_height", height)); // for when timstamp input is used. + + for ( auto notary : NotarisationNotaries ) + notaryarr.push_back(notary); + item.push_back(make_pair("notaries",notaryarr)); + kmd.push_back(item); + } + out.push_back(make_pair("KMD", kmd)); + //out.push_back(make_pair("LABS", labs)); + return out; +} + +/*UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) throw runtime_error("getNotarisationsForBlock blockHash\n\n" @@ -328,7 +824,7 @@ UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) out.push_back(item); } return out; -} +}*/ UniValue scanNotarisationsDB(const UniValue& params, bool fHelp) @@ -348,7 +844,7 @@ UniValue scanNotarisationsDB(const UniValue& params, bool fHelp) if (height == 0) { height = chainActive.Height(); } - + Notarisation nota; int matchedHeight = ScanNotarisationsDB(height, symbol, limit, nota); if (!matchedHeight) return NullUniValue; @@ -358,3 +854,116 @@ UniValue scanNotarisationsDB(const UniValue& params, bool fHelp) out.pushKV("opreturn", HexStr(E_MARSHAL(ss << nota.second))); return out; } + +UniValue getimports(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error( + "getimports \"hash|height\"\n" + "\n\n" + "\nResult:\n" + "{\n" + " \"imports\" : [ (json array)\n" + " \"transactionid\" : { (json object)\n" + " \"value\" : (numeric)\n" + " \"address\" : (string)\n" + " \"export\" { (json object)\n" + " \"txid\" : (string)\n" + " \"value\" : (numeric)\n" + " \"chain\" : (string)\n" + " }\n" + " }" + " ]\n" + " \"TotalImported\" : (numeric)\n" + " \"time\" : (numeric)\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("getimports", "\"00000000febc373a1da2bd9f887b105ad79ddc26ac26c2b28652d64e5207c5b5\"") + + HelpExampleRpc("getimports", "\"00000000febc373a1da2bd9f887b105ad79ddc26ac26c2b28652d64e5207c5b5\"") + + HelpExampleCli("getimports", "12800") + + HelpExampleRpc("getimports", "12800") + ); + + LOCK(cs_main); + + std::string strHash = params[0].get_str(); + + // If height is supplied, find the hash + if (strHash.size() < (2 * sizeof(uint256))) { + // std::stoi allows characters, whereas we want to be strict + regex r("[[:digit:]]+"); + if (!regex_match(strHash, r)) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); + } + + int nHeight = -1; + try { + nHeight = std::stoi(strHash); + } + catch (const std::exception &e) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid block height parameter"); + } + + if (nHeight < 0 || nHeight > chainActive.Height()) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); + } + strHash = chainActive[nHeight]->GetBlockHash().GetHex(); + } + + uint256 hash(uint256S(strHash)); + + if (mapBlockIndex.count(hash) == 0) + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); + + CBlock block; + CBlockIndex* pblockindex = mapBlockIndex[hash]; + + if (fHavePruned && !(pblockindex->nStatus & BLOCK_HAVE_DATA) && pblockindex->nTx > 0) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, pblockindex,1)) + throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); + + UniValue result(UniValue::VOBJ); + CAmount TotalImported = 0; + UniValue imports(UniValue::VARR); + BOOST_FOREACH(const CTransaction&tx, block.vtx) + { + if(tx.IsCoinImport()) + { + UniValue objTx(UniValue::VOBJ); + objTx.push_back(Pair("txid",tx.GetHash().ToString())); + TxProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; + TotalImported += tx.vout[0].nValue; + objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[0].nValue))); + if (ExtractDestination(tx.vout[0].scriptPubKey, importaddress)) + { + objTx.push_back(Pair("address", CBitcoinAddress(importaddress).ToString())); + } + UniValue objBurnTx(UniValue::VOBJ); + if (UnmarshalImportTx(tx, proof, burnTx, payouts)) + { + if (burnTx.vout.size() == 0) + continue; + objBurnTx.push_back(Pair("txid", burnTx.GetHash().ToString())); + objBurnTx.push_back(Pair("amount", ValueFromAmount(burnTx.vout.back().nValue))); + // extract op_return to get burn source chain. + std::vector burnOpret; std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash; std::vectorrawproof; + if (UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof)) + { + if (rawproof.size() > 0) + { + std::string sourceSymbol(rawproof.begin(), rawproof.end()); + objBurnTx.push_back(Pair("source", sourceSymbol)); + } + } + } + objTx.push_back(Pair("export", objBurnTx)); + imports.push_back(objTx); + } + } + result.push_back(Pair("imports", imports)); + result.push_back(Pair("TotalImported", TotalImported > 0 ? ValueFromAmount(TotalImported) : 0 )); + result.push_back(Pair("time", block.GetBlockTime())); + return result; +} diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index ff9b1b4c4..2efe8d694 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -49,6 +49,7 @@ using namespace std; #include "komodo_defs.h" +extern int32_t ASSETCHAINS_FOUNDERS; arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc); @@ -180,8 +181,11 @@ UniValue getgenerate(const UniValue& params, bool fHelp) LOCK(cs_main); UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("staking", VERUS_MINTBLOCKS)); - obj.push_back(Pair("generate", GetBoolArg("-gen", false))); + bool staking = VERUS_MINTBLOCKS; + if ( ASSETCHAINS_STAKED != 0 && GetBoolArg("-gen", false) && GetBoolArg("-genproclimit", -1) == 0 ) + staking = true; + obj.push_back(Pair("staking", staking)); + obj.push_back(Pair("generate", GetBoolArg("-gen", false) && GetBoolArg("-genproclimit", -1) != 0 )); obj.push_back(Pair("numthreads", (int64_t)KOMODO_MININGTHREADS)); return obj; } @@ -215,7 +219,18 @@ UniValue generate(const UniValue& params, bool fHelp) #endif } if (!Params().MineBlocksOnDemand()) - throw JSONRPCError(RPC_METHOD_NOT_FOUND, "This method can only be used on regtest"); + { + if ( params[0].get_int() == 1 ) + { + mapArgs["disablemining"] = "1"; + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Mining Disabled"); + } + else + { + mapArgs["disablemining"] = "0"; + throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Mining Enabled"); + } + } int nHeightStart = 0; int nHeightEnd = 0; @@ -345,7 +360,7 @@ UniValue setgenerate(const UniValue& params, bool fHelp) if (params.size() > 0) fGenerate = params[0].get_bool(); - int nGenProcLimit = GetArg("-genproclimit", -1);; + int nGenProcLimit = GetArg("-genproclimit", 0);; if (params.size() > 1) { nGenProcLimit = params[1].get_int(); @@ -436,8 +451,11 @@ UniValue getmininginfo(const UniValue& params, bool fHelp) obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); obj.push_back(Pair("chain", Params().NetworkIDString())); #ifdef ENABLE_MINING - obj.push_back(Pair("staking", VERUS_MINTBLOCKS)); - obj.push_back(Pair("generate", GetBoolArg("-gen", false))); + bool staking = VERUS_MINTBLOCKS; + if ( ASSETCHAINS_STAKED != 0 && GetBoolArg("-gen", false) && GetBoolArg("-genproclimit", -1) == 0 ) + staking = true; + obj.push_back(Pair("staking", staking)); + obj.push_back(Pair("generate", GetBoolArg("-gen", false) && GetBoolArg("-genproclimit", -1) != 0 )); obj.push_back(Pair("numthreads", (int64_t)KOMODO_MININGTHREADS)); #endif return obj; @@ -570,6 +588,9 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) throw JSONRPCError(RPC_METHOD_NOT_FOUND, "komodod compiled without wallet and -mineraddress not set"); #endif } + + if ( GetArg("disablemining",false) ) + throw JSONRPCError(RPC_TYPE_ERROR, "Mining is Disabled"); UniValue lpval = NullUniValue; // TODO: Re-enable coinbasevalue once a specification has been written @@ -714,10 +735,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) } #ifdef ENABLE_WALLET CReserveKey reservekey(pwalletMain); - pblocktemplate = CreateNewBlockWithKey(reservekey,chainActive.LastTip()->GetHeight()+1,KOMODO_MAXGPUCOUNT); + LEAVE_CRITICAL_SECTION(cs_main); + pblocktemplate = CreateNewBlockWithKey(reservekey,pindexPrevNew->GetHeight()+1,KOMODO_MAXGPUCOUNT,false); #else pblocktemplate = CreateNewBlockWithKey(); #endif + ENTER_CRITICAL_SECTION(cs_main); if (!pblocktemplate) throw JSONRPCError(RPC_OUT_OF_MEMORY, "Out of memory or no available utxo for staking"); @@ -763,10 +786,10 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp) if (tx.IsCoinBase() && coinbasetxn == true ) { // Show founders' reward if it is required - //if (pblock->vtx[0].vout.size() > 1) { + if (ASSETCHAINS_FOUNDERS && pblock->vtx[0].vout.size() > 1) { // Correct this if GetBlockTemplate changes the order - // entry.push_back(Pair("foundersreward", (int64_t)tx.vout[1].nValue)); - //} + entry.push_back(Pair("foundersreward", (int64_t)tx.vout[1].nValue)); + } CAmount nReward = GetBlockSubsidy(chainActive.LastTip()->GetHeight()+1, Params().GetConsensus()); entry.push_back(Pair("coinbasevalue", nReward)); entry.push_back(Pair("required", true)); diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index e6d8025b1..4e21ab9e9 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -28,7 +28,9 @@ #include "timedata.h" #include "txmempool.h" #include "util.h" +#include "notaries_staked.h" #include "cc/eval.h" +#include "cc/CCinclude.h" #ifdef ENABLE_WALLET #include "wallet/wallet.h" #include "wallet/walletdb.h" @@ -62,26 +64,131 @@ int32_t Jumblr_depositaddradd(char *depositaddr); int32_t Jumblr_secretaddradd(char *secretaddr); uint64_t komodo_interestsum(); int32_t komodo_longestchain(); -int32_t komodo_notarized_height(int32_t *prevhtp,uint256 *hashp,uint256 *txidp); +int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); bool komodo_txnotarizedconfirmed(uint256 txid); uint32_t komodo_chainactive_timestamp(); int32_t komodo_whoami(char *pubkeystr,int32_t height,uint32_t timestamp); extern uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; -extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN; +extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN,IS_STAKED_NOTARY,IS_KOMODO_NOTARY,STAKED_ERA; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t komodo_segid32(char *coinaddr); int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height); int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp); +int8_t StakedNotaryID(std::string ¬aryname, char *Raddress); + #define KOMODO_VERSION "0.3.3b" #define VERUS_VERSION "0.4.0g" extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; extern uint32_t ASSETCHAINS_CC; -extern uint32_t ASSETCHAINS_MAGIC; -extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_STAKED,ASSETCHAINS_SUPPLY; -extern uint32_t ASSETCHAINS_LASTERA; -extern int32_t ASSETCHAINS_LWMAPOS,ASSETCHAINS_SAPLING; -extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[]; -extern std::string NOTARY_PUBKEY; extern uint8_t NOTARY_PUBKEY33[]; +extern uint32_t ASSETCHAINS_MAGIC,ASSETCHAINS_ALGO; +extern uint64_t ASSETCHAINS_COMMISSION,ASSETCHAINS_SUPPLY; +extern int32_t ASSETCHAINS_LWMAPOS,ASSETCHAINS_SAPLING,ASSETCHAINS_STAKED; +extern uint64_t ASSETCHAINS_ENDSUBSIDY[],ASSETCHAINS_REWARD[],ASSETCHAINS_HALVING[],ASSETCHAINS_DECAY[],ASSETCHAINS_NOTARY_PAY[]; +extern std::string NOTARY_PUBKEY,NOTARY_ADDRESS; extern uint8_t NOTARY_PUBKEY33[]; + +int32_t getera(int timestamp) +{ + for (int32_t i = 0; i < NUM_STAKED_ERAS; i++) { + if ( timestamp <= STAKED_NOTARIES_TIMESTAMP[i] ) { + return(i); + } + } + return(0); +} + +UniValue getiguanajson(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error("getiguanajson\nreturns json for iguana, for the current ERA."); + + UniValue json(UniValue::VOBJ); + UniValue seeds(UniValue::VARR); + UniValue notaries(UniValue::VARR); + // get the current era, use local time for now. + // should ideally take blocktime of last known block? + int now = time(NULL); + int32_t era = getera(now); + + // loop over seeds array and push back to json array for seeds + for (int8_t i = 0; i < 8; i++) { + seeds.push_back(iguanaSeeds[i][0]); + } + + // loop over era's notaries and push back each pair to the notary array + for (int8_t i = 0; i < num_notaries_STAKED[era]; i++) { + UniValue notary(UniValue::VOBJ); + notary.push_back(Pair(notaries_STAKED[era][i][0],notaries_STAKED[era][i][1])); + notaries.push_back(notary); + } + + // get the min sigs .. this always rounds UP so min sigs in iguana is +1 min sigs in komodod, due to some rounding error. + int minsigs; + if ( num_notaries_STAKED[era]/5 > overrideMinSigs ) + minsigs = (num_notaries_STAKED[era] / 5) + 1; + else + minsigs = overrideMinSigs; + + json.push_back(Pair("port",iguanaPort)); + json.push_back(Pair("BTCminsigs",BTCminsigs)); + json.push_back(Pair("minsigs",minsigs)); + json.push_back(Pair("seeds",seeds)); + json.push_back(Pair("notaries",notaries)); + return json; +} + +UniValue getnotarysendmany(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() > 1) + throw runtime_error( + "getnotarysendmany\n" + "Returns a sendmany JSON array with all current notaries Raddress's.\n" + "\nExamples:\n" + + HelpExampleCli("getnotarysendmany", "10") + + HelpExampleRpc("getnotarysendmany", "10") + ); + int amount = 0; + if ( params.size() == 1 ) { + amount = params[0].get_int(); + } + + int era = getera(time(NULL)); + + UniValue ret(UniValue::VOBJ); + for (int i = 0; iGetHeight(); i++) + { + pindex = chainActive[i]; + era = getera(pindex->nTime)+1; + if ( era > lastera ) + { + char str[16]; + sprintf(str, "%d", era); + ret.push_back(Pair(str,(int64_t)i)); + lastera = era; + } + } + + return(ret); +} UniValue getinfo(const UniValue& params, bool fHelp) { @@ -179,17 +286,17 @@ UniValue getinfo(const UniValue& params, bool fHelp) #endif obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); obj.push_back(Pair("errors", GetWarnings("statusbar"))); - { - char pubkeystr[65]; int32_t notaryid; - if ( (notaryid= komodo_whoami(pubkeystr,(int32_t)chainActive.LastTip()->GetHeight(),komodo_chainactive_timestamp())) >= 0 ) - { + if ( NOTARY_PUBKEY33[0] != 0 ) { + char pubkeystr[65]; int32_t notaryid; std::string notaryname; + if ( (notaryid= StakedNotaryID(notaryname, (char *)NOTARY_ADDRESS.c_str())) != -1 ) { + obj.push_back(Pair("notaryid", notaryid)); + obj.push_back(Pair("notaryname", notaryname)); + } else if( (notaryid= komodo_whoami(pubkeystr,(int32_t)chainActive.LastTip()->GetHeight(),komodo_chainactive_timestamp())) >= 0 ) { obj.push_back(Pair("notaryid", notaryid)); - obj.push_back(Pair("pubkey", pubkeystr)); if ( KOMODO_LASTMINED != 0 ) - obj.push_back(Pair("lastmined", KOMODO_LASTMINED)); - } else if ( NOTARY_PUBKEY33[0] != 0 ) { - obj.push_back(Pair("pubkey", NOTARY_PUBKEY)); + obj.push_back(Pair("lastmined", KOMODO_LASTMINED)); } + obj.push_back(Pair("pubkey", NOTARY_PUBKEY)); } if ( ASSETCHAINS_CC != 0 ) obj.push_back(Pair("CCid", (int)ASSETCHAINS_CC)); @@ -200,13 +307,15 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("rpcport", ASSETCHAINS_RPCPORT)); if ( ASSETCHAINS_SYMBOL[0] != 0 ) { + if ( is_STAKED(ASSETCHAINS_SYMBOL) != 0 ) + obj.push_back(Pair("StakedEra", STAKED_ERA)); //obj.push_back(Pair("name", ASSETCHAINS_SYMBOL)); obj.push_back(Pair("magic", (int)ASSETCHAINS_MAGIC)); obj.push_back(Pair("premine", ASSETCHAINS_SUPPLY)); if ( ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_LASTERA > 0 ) { - std::string acReward = "", acHalving = "", acDecay = "", acEndSubsidy = ""; + std::string acReward = "", acHalving = "", acDecay = "", acEndSubsidy = "", acNotaryPay = ""; for (int i = 0; i <= ASSETCHAINS_LASTERA; i++) { if (i == 0) @@ -215,6 +324,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) acHalving = std::to_string(ASSETCHAINS_HALVING[i]); acDecay = std::to_string(ASSETCHAINS_DECAY[i]); acEndSubsidy = std::to_string(ASSETCHAINS_ENDSUBSIDY[i]); + acNotaryPay = std::to_string(ASSETCHAINS_NOTARY_PAY[i]); } else { @@ -222,6 +332,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) acHalving += "," + std::to_string(ASSETCHAINS_HALVING[i]); acDecay += "," + std::to_string(ASSETCHAINS_DECAY[i]); acEndSubsidy += "," + std::to_string(ASSETCHAINS_ENDSUBSIDY[i]); + acNotaryPay += "," + std::to_string(ASSETCHAINS_NOTARY_PAY[i]); } } if (ASSETCHAINS_LASTERA > 0) @@ -230,6 +341,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("halving", acHalving)); obj.push_back(Pair("decay", acDecay)); obj.push_back(Pair("endsubsidy", acEndSubsidy)); + obj.push_back(Pair("notarypay", acNotaryPay)); } if ( ASSETCHAINS_COMMISSION != 0 ) @@ -238,6 +350,8 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("staked", ASSETCHAINS_STAKED)); if ( ASSETCHAINS_LWMAPOS != 0 ) obj.push_back(Pair("veruspos", ASSETCHAINS_LWMAPOS)); + if ( ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH ) + obj.push_back(Pair("algo",ASSETCHAINS_ALGORITHMS[ASSETCHAINS_ALGO])); } return obj; } @@ -299,7 +413,7 @@ public: UniValue coinsupply(const UniValue& params, bool fHelp) { - int32_t height = 0; int32_t currentHeight; int64_t sproutfunds,zfunds,supply = 0; UniValue result(UniValue::VOBJ); + int32_t height = 0; int32_t currentHeight; int64_t blocks_per_year,zf1,zf3,zf12,sf1,sf3,sf12,sproutfunds,zfunds,supply1,supply3,supply12,supply = 0; UniValue result(UniValue::VOBJ); if (fHelp || params.size() > 1) throw runtime_error("coinsupply \n" "\nReturn coin supply information at a given block height. If no height is given, the current height is used.\n" @@ -334,6 +448,27 @@ UniValue coinsupply(const UniValue& params, bool fHelp) result.push_back(Pair("zfunds", ValueFromAmount(zfunds))); result.push_back(Pair("sprout", ValueFromAmount(sproutfunds))); result.push_back(Pair("total", ValueFromAmount(zfunds + supply))); + if ( ASSETCHAINS_BLOCKTIME > 0 ) + { + blocks_per_year = 24*3600*365 / ASSETCHAINS_BLOCKTIME; + if ( height > blocks_per_year ) + { + supply1 = komodo_coinsupply(&zf1,&sf1,height - blocks_per_year/12); + supply3 = komodo_coinsupply(&zf3,&sf3,height - blocks_per_year/4); + supply12 = komodo_coinsupply(&zf12,&sf12,height - blocks_per_year); + if ( supply1 != 0 && supply3 != 0 && supply12 != 0 ) + { + result.push_back(Pair("lastmonth", ValueFromAmount(supply1+zf1))); + result.push_back(Pair("monthcoins", ValueFromAmount(zfunds + supply - supply1-zf1))); + result.push_back(Pair("lastquarter", ValueFromAmount(supply3+zf3))); + result.push_back(Pair("quartercoins", ValueFromAmount(zfunds + supply - supply3-zf3))); + result.push_back(Pair("lastyear", ValueFromAmount(supply12+zf12))); + result.push_back(Pair("yearcoins", ValueFromAmount(zfunds + supply - supply12-zf12))); + result.push_back(Pair("inflation", 100. * (((double)(zfunds + supply)/(supply12+zf12))-1.))); + result.push_back(Pair("blocksperyear", (int64_t)blocks_per_year)); + } + } + } } else result.push_back(Pair("error", "couldnt calculate supply")); } else { result.push_back(Pair("error", "invalid height")); @@ -739,8 +874,9 @@ bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &addr address = CBitcoinAddress(CScriptID(hash)).ToString(); } else if (type == 1) { address = CBitcoinAddress(CKeyID(hash)).ToString(); - } - else { + } else if (type == 3) { + address = CBitcoinAddress(CKeyID(hash)).ToString(); + } else { return false; } return true; @@ -748,11 +884,14 @@ bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &addr bool getAddressesFromParams(const UniValue& params, std::vector > &addresses) { + bool ccVout = false; + if (params.size() == 2) + ccVout = true; if (params[0].isStr()) { CBitcoinAddress address(params[0].get_str()); uint160 hashBytes; int type = 0; - if (!address.GetIndexKey(hashBytes, type)) { + if (!address.GetIndexKey(hashBytes, type, ccVout)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); } addresses.push_back(std::make_pair(hashBytes, type)); @@ -770,7 +909,7 @@ bool getAddressesFromParams(const UniValue& params, std::vectorget_str()); uint160 hashBytes; int type = 0; - if (!address.GetIndexKey(hashBytes, type)) { + if (!address.GetIndexKey(hashBytes, type, ccVout)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid addresses"); } addresses.push_back(std::make_pair(hashBytes, type)); @@ -794,7 +933,7 @@ bool timestampSort(std::pair a, UniValue getaddressmempool(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() > 2 || params.size() == 0) throw runtime_error( "getaddressmempool\n" "\nReturns all mempool deltas for an address (requires addressindex to be enabled).\n" @@ -806,6 +945,7 @@ UniValue getaddressmempool(const UniValue& params, bool fHelp) " ,...\n" " ]\n" "}\n" + "\nCCvout (optional) Return CCvouts instead of normal vouts\n" "\nResult:\n" "[\n" " {\n" @@ -819,8 +959,8 @@ UniValue getaddressmempool(const UniValue& params, bool fHelp) " }\n" "]\n" "\nExamples:\n" - + HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'") - + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}") + + HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}' (ccvout)") + + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]} (ccvout)") ); std::vector > addresses; @@ -865,7 +1005,7 @@ UniValue getaddressmempool(const UniValue& params, bool fHelp) UniValue getaddressutxos(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() > 2 || params.size() == 0) throw runtime_error( "getaddressutxos\n" "\nReturns all unspent outputs for an address (requires addressindex to be enabled).\n" @@ -878,6 +1018,7 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) " ],\n" " \"chainInfo\" (boolean) Include chain info with results\n" "}\n" + "\nCCvout (optional) Return CCvouts instead of normal vouts\n" "\nResult\n" "[\n" " {\n" @@ -890,8 +1031,8 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) " }\n" "]\n" "\nExamples:\n" - + HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'") - + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}") + + HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}' (ccvout)") + + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]} (ccvout)") ); bool includeChainInfo = false; @@ -951,7 +1092,7 @@ UniValue getaddressutxos(const UniValue& params, bool fHelp) UniValue getaddressdeltas(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 1 || !params[0].isObject()) + if (fHelp || params.size() > 2 || params.size() == 0 || !params[0].isObject()) throw runtime_error( "getaddressdeltas\n" "\nReturns all changes for an address (requires addressindex to be enabled).\n" @@ -966,6 +1107,7 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) " \"end\" (number) The end block height\n" " \"chainInfo\" (boolean) Include chain info in results, only applies if start and end specified\n" "}\n" + "\nCCvout (optional) Return CCvouts instead of normal vouts\n" "\nResult:\n" "[\n" " {\n" @@ -977,8 +1119,8 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) " }\n" "]\n" "\nExamples:\n" - + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'") - + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}") + + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}' (ccvout)") + + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]} (ccvout)") ); @@ -1076,7 +1218,7 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) UniValue getaddressbalance(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp ||params.size() > 2 || params.size() == 0) throw runtime_error( "getaddressbalance\n" "\nReturns the balance for an address(es) (requires addressindex to be enabled).\n" @@ -1088,14 +1230,15 @@ UniValue getaddressbalance(const UniValue& params, bool fHelp) " ,...\n" " ]\n" "}\n" + "\nCCvout (optional) Return CCvouts instead of normal vouts\n" "\nResult:\n" "{\n" " \"balance\" (string) The current balance in satoshis\n" " \"received\" (string) The total number of satoshis received (including change)\n" "}\n" "\nExamples:\n" - + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'") - + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}") + + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}' (ccvout)") + + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]} (ccvout)") ); std::vector > addresses; @@ -1186,9 +1329,9 @@ UniValue getsnapshot(const UniValue& params, bool fHelp) UniValue getaddresstxids(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 1) + if (fHelp || params.size() > 2) throw runtime_error( - "getaddresstxids\n" + "getaddresstxids (ccvout)\n" "\nReturns the txids for an address(es) (requires addressindex to be enabled).\n" "\nArguments:\n" "{\n" @@ -1200,14 +1343,15 @@ UniValue getaddresstxids(const UniValue& params, bool fHelp) " \"start\" (number) The start block height\n" " \"end\" (number) The end block height\n" "}\n" + "\nCCvout (optional) Return CCvouts instead of normal vouts\n" "\nResult:\n" "[\n" " \"transactionid\" (string) The transaction id\n" " ,...\n" "]\n" "\nExamples:\n" - + HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}'") - + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}") + + HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]}' (ccvout)") + + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"RY5LccmGiX9bUHYGtSWQouNy1yFhc5rM87\"]} (ccvout)") ); std::vector > addresses; @@ -1322,15 +1466,15 @@ UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp) if (fHelp || params.size() < 1 || params.size() > 1) { string msg = "txnotarizedconfirmed txid\n" - "\nReturns true if transaction is notarized on chain that has dPoW or if confirmation number is greater than 60 on chain taht does not have dPoW.\n" + "\nReturns true if transaction is notarized on chain that has dPoW or if confirmation number is greater than 60 on chain taht does not have dPoW.\n" "\nArguments:\n" - "1. txid (string, required) Transaction id.\n" + "1. txid (string, required) Transaction id.\n" "\nResult:\n" "{\n" - " true, (bool) The value the check.\n" - "}\n" + " true, (bool) The value the check.\n" + "}\n" ; throw runtime_error(msg); } @@ -1343,17 +1487,17 @@ UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp) UniValue decodeccopret(const UniValue& params, bool fHelp) { - CTransaction tx; uint256 txid,hashBlock; - std::vector vopret; uint8_t *script; - UniValue result(UniValue::VOBJ); + CTransaction tx; uint256 tokenid,txid,hashblock; + std::vector vopret,vOpretExtra; uint8_t *script,tokenevalcode; + UniValue result(UniValue::VOBJ),array(UniValue::VARR); std::vector pubkeys; if (fHelp || params.size() < 1 || params.size() > 1) { - string msg = "decodeccopret hex\n" + string msg = "decodeccopret scriptPubKey\n" "\nReturns eval code and function id for CC OP RETURN data.\n" "\nArguments:\n" - "1. txid (string, required) Transaction id.\n" + "1. scriptPubKey (string, required) Hex of scriptPubKey with OP_RETURN data.\n" "\nResult:\n" "{\n" @@ -1363,21 +1507,41 @@ UniValue decodeccopret(const UniValue& params, bool fHelp) ; throw runtime_error(msg); } - txid = uint256S((char *)params[0].get_str().c_str()); + std::vector hex(ParseHex(params[0].get_str())); + CScript scripthex(hex.begin(),hex.end()); + std::vector> oprets; + if (DecodeTokenOpRet(scripthex,tokenevalcode,tokenid,pubkeys, oprets)!=0 && tokenevalcode==EVAL_TOKENS && oprets.size()>0) { - LOCK(cs_main); - if (!GetTransaction(txid, tx, hashBlock, true)) - throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); + // seems we need a loop here + vOpretExtra = oprets[0].second; + UniValue obj(UniValue::VOBJ); + GetOpReturnData(scripthex,vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 1) + { + char func[5]; + sprintf(func,"%c",script[1]); + obj.push_back(Pair("eval_code", EvalToStr(script[0]))); + obj.push_back(Pair("function", func)); + } + else + { + obj.push_back(Pair("error", "invalid or no CC opret data for Token OP_RETURN")); + } + array.push_back(obj); + if (!E_UNMARSHAL(vOpretExtra, { ss >> vopret; })) return (0); } - GetOpReturnData(tx.vout[tx.vout.size()-1].scriptPubKey,vopret); + else GetOpReturnData(scripthex,vopret); script = (uint8_t *)vopret.data(); if ( vopret.size() > 1) { - char func[5]; - sprintf(func,"%c",script[1]); + char func[5]; UniValue obj(UniValue::VOBJ); result.push_back(Pair("result", "success")); - result.push_back(Pair("eval_code", EvalToStr(script[0]))); - result.push_back(Pair("function", func)); + sprintf(func,"%c",script[1]); + obj.push_back(Pair("eval_code", EvalToStr(script[0]))); + obj.push_back(Pair("function", func)); + array.push_back(obj); + result.push_back(Pair("OpRets",array)); } else { diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 5ab040711..6e90c4db5 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -35,6 +35,7 @@ #include "script/sign.h" #include "script/standard.h" #include "uint256.h" +#include "importcoin.h" #ifdef ENABLE_WALLET #include "wallet/wallet.h" #endif @@ -202,6 +203,25 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); else if (tx.IsCoinImport()) { in.push_back(Pair("is_import", "1")); + TxProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; + if (UnmarshalImportTx(tx, proof, burnTx, payouts)) + { + if (burnTx.vout.size() == 0) + continue; + in.push_back(Pair("txid", burnTx.GetHash().ToString())); + in.push_back(Pair("value", ValueFromAmount(burnTx.vout.back().nValue))); + in.push_back(Pair("valueSat", burnTx.vout.back().nValue)); + // extract op_return to get burn source chain. + std::vector burnOpret; std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash; std::vectorrawproof; + if (UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof)) + { + if (rawproof.size() > 0) + { + std::string sourceSymbol(rawproof.begin(), rawproof.end()); + in.push_back(Pair("address", "IMP-" + sourceSymbol + "-" + burnTx.GetHash().ToString())); + } + } + } } else { in.push_back(Pair("txid", txin.prevout.hash.GetHex())); @@ -256,6 +276,14 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& out.push_back(Pair("n", (int64_t)i)); UniValue o(UniValue::VOBJ); ScriptPubKeyToJSON(txout.scriptPubKey, o, true); + if (txout.scriptPubKey.IsOpReturn() && txout.nValue != 0) + { + std::vector burnOpret; std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash; std::vectorrawproof; + if (UnmarshalBurnTx(tx, targetSymbol, &targetCCid, payoutsHash, rawproof)) + { + out.push_back(Pair("target", "EXPORT->" + targetSymbol)); + } + } out.push_back(Pair("scriptPubKey", o)); // Add spent information if spentindex is enabled @@ -565,7 +593,7 @@ int32_t gettxout_scriptPubKey(uint8_t *scriptPubKey,int32_t maxsize,uint256 txid uint256 hashBlock; if ( GetTransaction(txid,tx,hashBlock,false) == 0 ) return(-1); - else if ( n < tx.vout.size() ) + else if ( n < tx.vout.size() ) { ptr = (uint8_t *)&tx.vout[n].scriptPubKey[0]; m = tx.vout[n].scriptPubKey.size(); @@ -1304,7 +1332,7 @@ UniValue sendrawtransaction(const UniValue& params, bool fHelp) } } else if (fHaveChain) { throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); - } + } RelayTransaction(tx); return hashTx.GetHex(); diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 82a400e71..fc34a2a3f 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -291,6 +291,9 @@ static const CRPCCommand vRPCCommands[] = // --------------------- ------------------------ ----------------------- ---------- /* Overall control/query calls */ { "control", "help", &help, true }, + { "control", "getiguanajson", &getiguanajson, true }, + { "control", "getnotarysendmany", &getnotarysendmany, true }, + { "control", "geterablockheights", &geterablockheights, true }, { "control", "stop", &stop, true }, /* P2P networking */ @@ -346,10 +349,26 @@ static const CRPCCommand vRPCCommands[] = { "crosschain", "crosschainproof", &crosschainproof, true }, { "crosschain", "getNotarisationsForBlock", &getNotarisationsForBlock, true }, { "crosschain", "scanNotarisationsDB", &scanNotarisationsDB, true }, + { "crosschain", "getimports", &getimports, true }, { "crosschain", "migrate_converttoexport", &migrate_converttoexport, true }, { "crosschain", "migrate_createimporttransaction", &migrate_createimporttransaction, true }, { "crosschain", "migrate_completeimporttransaction", &migrate_completeimporttransaction, true }, { "crosschain", "selfimport", &selfimport, true }, + { "crosschain", "importdual", &importdual, true }, + //ImportGateway + { "crosschain", "importgatewayddress", &importgatewayaddress, true }, + { "crosschain", "importgatewayinfo", &importgatewayinfo, true }, + { "crosschain", "importgatewaybind", &importgatewaybind, true }, + { "crosschain", "importgatewaydeposit", &importgatewaydeposit, true }, + { "crosschain", "importgatewaywithdraw", &importgatewaywithdraw, true }, + { "crosschain", "importgatewaypartialsign", &importgatewaypartialsign, true }, + { "crosschain", "importgatewaycompletesigning", &importgatewaycompletesigning, true }, + { "crosschain", "importgatewaymarkdone", &importgatewaymarkdone, true }, + { "crosschain", "importgatewaypendingdeposits", &importgatewaypendingdeposits, true }, + { "crosschain", "importgatewaypendingwithdraws", &importgatewaypendingwithdraws, true }, + { "crosschain", "importgatewayprocessed", &importgatewayprocessed, true }, + + /* Mining */ { "mining", "getblocktemplate", &getblocktemplate, true }, @@ -437,14 +456,10 @@ static const CRPCCommand vRPCCommands[] = { "oracles", "oraclessamples", &oraclessamples, true }, // Prices + { "prices", "prices", &prices, true }, { "prices", "pricesaddress", &pricesaddress, true }, { "prices", "priceslist", &priceslist, true }, { "prices", "pricesinfo", &pricesinfo, true }, - { "prices", "pricescreate", &pricescreate, true }, - { "prices", "pricesaddfunding", &pricesaddfunding, true }, - { "prices", "pricesbet", &pricesbet, true }, - { "prices", "pricesstatus", &pricesstatus, true }, - { "prices", "pricesfinish", &pricesfinish, true }, // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, @@ -462,6 +477,12 @@ static const CRPCCommand vRPCCommands[] = // Payments { "payments", "paymentsaddress", &paymentsaddress, true }, + { "payments", "paymentstxidopret", &payments_txidopret, true }, + { "payments", "paymentscreate", &payments_create, true }, + { "payments", "paymentslist", &payments_list, true }, + { "payments", "paymentsinfo", &payments_info, true }, + { "payments", "paymentsfund", &payments_fund, true }, + { "payments", "paymentsrelease", &payments_release, true }, { "CClib", "cclibaddress", &cclibaddress, true }, { "CClib", "cclibinfo", &cclibinfo, true }, @@ -470,6 +491,8 @@ static const CRPCCommand vRPCCommands[] = // Gateways { "gateways", "gatewaysaddress", &gatewaysaddress, true }, { "gateways", "gatewayslist", &gatewayslist, true }, + { "gateways", "gatewaysexternaladdress", &gatewaysexternaladdress, true }, + { "gateways", "gatewaysdumpprivkey", &gatewaysdumpprivkey, true }, { "gateways", "gatewaysinfo", &gatewaysinfo, true }, { "gateways", "gatewaysbind", &gatewaysbind, true }, { "gateways", "gatewaysdeposit", &gatewaysdeposit, true }, @@ -478,9 +501,9 @@ static const CRPCCommand vRPCCommands[] = { "gateways", "gatewayspartialsign", &gatewayspartialsign, true }, { "gateways", "gatewayscompletesigning", &gatewayscompletesigning, true }, { "gateways", "gatewaysmarkdone", &gatewaysmarkdone, true }, - { "gateways", "gatewayspending", &gatewayspending, true }, + { "gateways", "gatewayspendingdeposits", &gatewayspendingdeposits, true }, + { "gateways", "gatewayspendingwithdraws", &gatewayspendingwithdraws, true }, { "gateways", "gatewaysprocessed", &gatewaysprocessed, true }, - { "gateways", "gatewaysmultisig", &gatewaysmultisig, true }, // dice { "dice", "dicelist", &dicelist, true }, @@ -497,6 +520,7 @@ static const CRPCCommand vRPCCommands[] = { "tokens", "tokeninfo", &tokeninfo, true }, { "tokens", "tokenlist", &tokenlist, true }, { "tokens", "tokenorders", &tokenorders, true }, + { "tokens", "mytokenorders", &mytokenorders, true }, { "tokens", "tokenaddress", &tokenaddress, true }, { "tokens", "tokenbalance", &tokenbalance, true }, { "tokens", "tokencreate", &tokencreate, true }, @@ -538,7 +562,10 @@ static const CRPCCommand vRPCCommands[] = /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, true }, { "hidden", "test_ac", &test_ac, true }, - { "hidden", "test_heirmarker", &test_heirmarker, true }, + { "hidden", "test_heirmarker", &test_heirmarker, true }, + { "hidden", "test_proof", &test_proof, true }, + { "hidden", "test_burntx", &test_burntx, true }, + #ifdef ENABLE_WALLET /* Wallet */ @@ -551,6 +578,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "getaccountaddress", &getaccountaddress, true }, { "wallet", "getaccount", &getaccount, true }, { "wallet", "getaddressesbyaccount", &getaddressesbyaccount, true }, + { "wallet", "cleanwallettransactions", &cleanwallettransactions, false }, { "wallet", "getbalance", &getbalance, false }, { "wallet", "getbalance64", &getbalance64, false }, { "wallet", "getnewaddress", &getnewaddress, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 3edc85ebf..d8fd0e736 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -241,6 +241,7 @@ extern UniValue coinsupply(const UniValue& params, bool fHelp); extern UniValue tokeninfo(const UniValue& params, bool fHelp); extern UniValue tokenlist(const UniValue& params, bool fHelp); extern UniValue tokenorders(const UniValue& params, bool fHelp); +extern UniValue mytokenorders(const UniValue& params, bool fHelp); extern UniValue tokenbalance(const UniValue& params, bool fHelp); extern UniValue assetsaddress(const UniValue& params, bool fHelp); extern UniValue tokenaddress(const UniValue& params, bool fHelp); @@ -271,11 +272,6 @@ extern UniValue oraclessamples(const UniValue& params, bool fHelp); extern UniValue pricesaddress(const UniValue& params, bool fHelp); extern UniValue priceslist(const UniValue& params, bool fHelp); extern UniValue pricesinfo(const UniValue& params, bool fHelp); -extern UniValue pricescreate(const UniValue& params, bool fHelp); -extern UniValue pricesaddfunding(const UniValue& params, bool fHelp); -extern UniValue pricesbet(const UniValue& params, bool fHelp); -extern UniValue pricesstatus(const UniValue& params, bool fHelp); -extern UniValue pricesfinish(const UniValue& params, bool fHelp); extern UniValue pegsaddress(const UniValue& params, bool fHelp); extern UniValue marmaraaddress(const UniValue& params, bool fHelp); extern UniValue marmara_poolpayout(const UniValue& params, bool fHelp); @@ -287,12 +283,21 @@ extern UniValue marmara_creditloop(const UniValue& params, bool fHelp); extern UniValue marmara_settlement(const UniValue& params, bool fHelp); extern UniValue marmara_lock(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); +extern UniValue payments_release(const UniValue& params, bool fHelp); +extern UniValue payments_fund(const UniValue& params, bool fHelp); +extern UniValue payments_txidopret(const UniValue& params, bool fHelp); +extern UniValue payments_create(const UniValue& params, bool fHelp); +extern UniValue payments_info(const UniValue& params, bool fHelp); +extern UniValue payments_list(const UniValue& params, bool fHelp); + extern UniValue cclibaddress(const UniValue& params, bool fHelp); extern UniValue cclibinfo(const UniValue& params, bool fHelp); extern UniValue cclib(const UniValue& params, bool fHelp); extern UniValue gatewaysaddress(const UniValue& params, bool fHelp); extern UniValue gatewayslist(const UniValue& params, bool fHelp); extern UniValue gatewaysinfo(const UniValue& params, bool fHelp); +extern UniValue gatewaysdumpprivkey(const UniValue& params, bool fHelp); +extern UniValue gatewaysexternaladdress(const UniValue& params, bool fHelp); extern UniValue gatewaysbind(const UniValue& params, bool fHelp); extern UniValue gatewaysdeposit(const UniValue& params, bool fHelp); extern UniValue gatewaysclaim(const UniValue& params, bool fHelp); @@ -300,16 +305,15 @@ extern UniValue gatewayswithdraw(const UniValue& params, bool fHelp); extern UniValue gatewayspartialsign(const UniValue& params, bool fHelp); extern UniValue gatewayscompletesigning(const UniValue& params, bool fHelp); extern UniValue gatewaysmarkdone(const UniValue& params, bool fHelp); -extern UniValue gatewayspending(const UniValue& params, bool fHelp); +extern UniValue gatewayspendingdeposits(const UniValue& params, bool fHelp); +extern UniValue gatewayspendingwithdraws(const UniValue& params, bool fHelp); extern UniValue gatewaysprocessed(const UniValue& params, bool fHelp); -extern UniValue gatewaysmultisig(const UniValue& params, bool fHelp); extern UniValue channelslist(const UniValue& params, bool fHelp); extern UniValue channelsinfo(const UniValue& params, bool fHelp); extern UniValue channelsopen(const UniValue& params, bool fHelp); extern UniValue channelspayment(const UniValue& params, bool fHelp); extern UniValue channelsclose(const UniValue& params, bool fHelp); extern UniValue channelsrefund(const UniValue& params, bool fHelp); - //extern UniValue tokenswapask(const UniValue& params, bool fHelp); //extern UniValue tokenfillswap(const UniValue& params, bool fHelp); extern UniValue faucetfund(const UniValue& params, bool fHelp); @@ -350,6 +354,7 @@ extern UniValue signmessage(const UniValue& params, bool fHelp); extern UniValue verifymessage(const UniValue& params, bool fHelp); extern UniValue getreceivedbyaddress(const UniValue& params, bool fHelp); extern UniValue getreceivedbyaccount(const UniValue& params, bool fHelp); +extern UniValue cleanwallettransactions(const UniValue& params, bool fHelp); extern UniValue getbalance(const UniValue& params, bool fHelp); extern UniValue getbalance64(const UniValue& params, bool fHelp); extern UniValue getunconfirmedbalance(const UniValue& params, bool fHelp); @@ -375,6 +380,9 @@ extern UniValue validateaddress(const UniValue& params, bool fHelp); extern UniValue txnotarizedconfirmed(const UniValue& params, bool fHelp); extern UniValue decodeccopret(const UniValue& params, bool fHelp); extern UniValue getinfo(const UniValue& params, bool fHelp); +extern UniValue getiguanajson(const UniValue& params, bool fHelp); +extern UniValue getnotarysendmany(const UniValue& params, bool fHelp); +extern UniValue geterablockheights(const UniValue& params, bool fHelp); extern UniValue setpubkey(const UniValue& params, bool fHelp); extern UniValue getwalletinfo(const UniValue& params, bool fHelp); extern UniValue getblockchaininfo(const UniValue& params, bool fHelp); @@ -426,6 +434,18 @@ extern UniValue invalidateblock(const UniValue& params, bool fHelp); extern UniValue reconsiderblock(const UniValue& params, bool fHelp); extern UniValue getspentinfo(const UniValue& params, bool fHelp); extern UniValue selfimport(const UniValue& params, bool fHelp); +extern UniValue importdual(const UniValue& params, bool fHelp); +extern UniValue importgatewayaddress(const UniValue& params, bool fHelp); +extern UniValue importgatewayinfo(const UniValue& params, bool fHelp); +extern UniValue importgatewaybind(const UniValue& params, bool fHelp); +extern UniValue importgatewaydeposit(const UniValue& params, bool fHelp); +extern UniValue importgatewaywithdraw(const UniValue& params, bool fHelp); +extern UniValue importgatewaypartialsign(const UniValue& params, bool fHelp); +extern UniValue importgatewaycompletesigning(const UniValue& params, bool fHelp); +extern UniValue importgatewaymarkdone(const UniValue& params, bool fHelp); +extern UniValue importgatewaypendingdeposits(const UniValue& params, bool fHelp); +extern UniValue importgatewaypendingwithdraws(const UniValue& params, bool fHelp); +extern UniValue importgatewayprocessed(const UniValue& params, bool fHelp); extern UniValue getblocksubsidy(const UniValue& params, bool fHelp); @@ -457,6 +477,7 @@ extern UniValue assetchainproof(const UniValue& params, bool fHelp); extern UniValue crosschainproof(const UniValue& params, bool fHelp); extern UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp); extern UniValue scanNotarisationsDB(const UniValue& params, bool fHelp); +extern UniValue getimports(const UniValue& params, bool fHelp); extern UniValue migrate_converttoexport(const UniValue& params, bool fHelp); extern UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp); extern UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp); @@ -471,8 +492,12 @@ extern UniValue paxprices(const UniValue& params, bool fHelp); extern UniValue paxdeposit(const UniValue& params, bool fHelp); extern UniValue paxwithdraw(const UniValue& params, bool fHelp); +extern UniValue prices(const UniValue& params, bool fHelp); + // test rpc: extern UniValue test_ac(const UniValue& params, bool fHelp); extern UniValue test_heirmarker(const UniValue& params, bool fHelp); +extern UniValue test_burntx(const UniValue& params, bool fHelp); +extern UniValue test_proof(const UniValue& params, bool fHelp); #endif // BITCOIN_RPCSERVER_H diff --git a/src/rpcblockchain.old b/src/rpcblockchain.old index 359df15ce..a91f73a63 100644 --- a/src/rpcblockchain.old +++ b/src/rpcblockchain.old @@ -836,7 +836,7 @@ UniValue kvsearch(const UniValue& params, bool fHelp) " \"currentheight\": xxxxx, (numeric) current height of the chain\n" " \"key\": \"xxxxx\", (string) key\n" " \"keylen\": xxxxx, (string) length of the key \n" - " \"owner\": \"xxxxx\" (string) hex string representing the owner of the key \n" + " \"owner\": \"xxxxx\" (string) hex string representing the owner of the key \n" " \"height\": xxxxx, (numeric) height the key was stored at\n" " \"expiration\": xxxxx, (numeric) height the key will expire\n" " \"flags\": x (numeric) 1 if the key was created with a password; 0 otherwise.\n" diff --git a/src/scheduler.cpp b/src/scheduler.cpp index e085791fc..08aba3225 100644 --- a/src/scheduler.cpp +++ b/src/scheduler.cpp @@ -31,6 +31,17 @@ CScheduler::CScheduler() : nThreadsServicingQueue(0), stopRequested(false), stop CScheduler::~CScheduler() { + /*int32_t i; + if ( nThreadsServicingQueue != 0 ) + { + for (i=0; i<10; i++) + { + sleep(1); + fprintf(stderr,"CScheduler nThreadsServicingQueue.%d\n",(int32_t)nThreadsServicingQueue); + if ( nThreadsServicingQueue == 0 ) + break; + } + }*/ assert(nThreadsServicingQueue == 0); } diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 2b81abdc8..d38c0a351 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -407,7 +407,7 @@ bool ExtractDestination(const CScript& _scriptPubKey, CTxDestination& addressRet addressRet = CScriptID(uint160(vSolutions[0])); return true; } - + else if (IsCryptoConditionsEnabled() != 0 && whichType == TX_CRYPTOCONDITION) { if (vSolutions.size() > 1) diff --git a/src/secp256k1/Makefile.am b/src/secp256k1/Makefile.am index c071fbe27..c5fa00fc5 100644 --- a/src/secp256k1/Makefile.am +++ b/src/secp256k1/Makefile.am @@ -42,6 +42,8 @@ noinst_HEADERS += src/field_5x52_asm_impl.h noinst_HEADERS += src/java/org_bitcoin_NativeSecp256k1.h noinst_HEADERS += src/java/org_bitcoin_Secp256k1Context.h noinst_HEADERS += src/util.h +noinst_HEADERS += src/scratch.h +noinst_HEADERS += src/scratch_impl.h noinst_HEADERS += src/testrand.h noinst_HEADERS += src/testrand_impl.h noinst_HEADERS += src/hash.h @@ -71,7 +73,7 @@ endif endif libsecp256k1_la_SOURCES = src/secp256k1.c -libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) +libsecp256k1_la_CPPFLAGS = -DSECP256K1_BUILD -DENABLE_MODULE_MUSIG -I$(top_srcdir)/include -I$(top_srcdir)/src $(SECP_INCLUDES) libsecp256k1_la_LIBADD = $(JNI_LIB) $(SECP_LIBS) $(COMMON_LIB) libsecp256k1_jni_la_SOURCES = src/java/org_bitcoin_NativeSecp256k1.c src/java/org_bitcoin_Secp256k1Context.c diff --git a/src/secp256k1/include/secp256k1.h b/src/secp256k1/include/secp256k1.h index 3e9c098d1..7dcdd7d0b 100644 --- a/src/secp256k1/include/secp256k1.h +++ b/src/secp256k1/include/secp256k1.h @@ -42,6 +42,19 @@ extern "C" { */ typedef struct secp256k1_context_struct secp256k1_context; +/** Opaque data structure that holds rewriteable "scratch space" + * + * The purpose of this structure is to replace dynamic memory allocations, + * because we target architectures where this may not be available. It is + * essentially a resizable (within specified parameters) block of bytes, + * which is initially created either by memory allocation or TODO as a pointer + * into some fixed rewritable space. + * + * Unlike the context object, this cannot safely be shared between threads + * without additional synchronization logic. + */ +typedef struct secp256k1_scratch_space_struct secp256k1_scratch_space; + /** Opaque data structure that holds a parsed and valid public key. * * The exact representation of data inside is implementation defined and not diff --git a/src/secp256k1/include/secp256k1_musig.h b/src/secp256k1/include/secp256k1_musig.h new file mode 100644 index 000000000..84106822a --- /dev/null +++ b/src/secp256k1/include/secp256k1_musig.h @@ -0,0 +1,502 @@ +#ifndef SECP256K1_MUSIG_H +#define SECP256K1_MUSIG_H + +#include + +/** This module implements a Schnorr-based multi-signature scheme called MuSig + * (https://eprint.iacr.org/2018/068.pdf). There's an example C source file in the + * module's directory (src/modules/musig/example.c) that demonstrates how it can be + * used. + */ + +/** Data structure containing data related to a signing session resulting in a single + * signature. + * + * This structure is not opaque, but it MUST NOT be copied or read or written to it + * directly. A signer who is online throughout the whole process and can keep this + * structure in memory can use the provided API functions for a safe standard + * workflow. + * + * A signer who goes offline and needs to import/export or save/load this structure + * **must** take measures prevent replay attacks wherein an old state is loaded and + * the signing protocol forked from that point. One straightforward way to accomplish + * this is to attach the output of a monotonic non-resettable counter (hardware + * support is needed for this). Increment the counter before each output and + * encrypt+sign the entire package. If a package is deserialized with an old counter + * state or bad signature it should be rejected. + * + * Observe that an independent counter is needed for each concurrent signing session + * such a device is involved in. To avoid fragility, it is therefore recommended that + * any offline signer be usable for only a single session at once. + * + * Given access to such a counter, its output should be used as (or mixed into) the + * session ID to ensure uniqueness. + * + * Fields: + * combined_pk: MuSig-computed combined public key + * n_signers: Number of signers + * pk_hash: The 32-byte hash of the original public keys + * combined_nonce: Summed combined public nonce (undefined if `nonce_is_set` is false) + * nonce_is_set: Whether the above nonce has been set + * nonce_is_negated: If `nonce_is_set`, whether the above nonce was negated after + * summing the participants' nonces. Needed to ensure the nonce's y + * coordinate has a quadratic-residue y coordinate + * msg: The 32-byte message (hash) to be signed + * msg_is_set: Whether the above message has been set + * has_secret_data: Whether this session object has a signers' secret data; if this + * is `false`, it may still be used for verification purposes. + * seckey: If `has_secret_data`, the signer's secret key + * secnonce: If `has_secret_data`, the signer's secret nonce + * nonce: If `has_secret_data`, the signer's public nonce + * nonce_commitments_hash: If `has_secret_data` and `nonce_commitments_hash_is_set`, + * the hash of all signers' commitments + * nonce_commitments_hash_is_set: If `has_secret_data`, whether the + * nonce_commitments_hash has been set + */ +typedef struct { + secp256k1_pubkey combined_pk; + uint32_t n_signers; + unsigned char pk_hash[32]; + secp256k1_pubkey combined_nonce; + int nonce_is_set; + int nonce_is_negated; + unsigned char msg[32]; + int msg_is_set; + int has_secret_data; + unsigned char seckey[32]; + unsigned char secnonce[32]; + secp256k1_pubkey nonce; + unsigned char nonce_commitments_hash[32]; + int nonce_commitments_hash_is_set; +} secp256k1_musig_session; + +/** Data structure containing data on all signers in a single session. + * + * The workflow for this structure is as follows: + * + * 1. This structure is initialized with `musig_session_initialize` or + * `musig_session_initialize_verifier`, which set the `index` field, and zero out + * all other fields. The public session is initialized with the signers' + * nonce_commitments. + * + * 2. In a non-public session the nonce_commitments are set with the function + * `musig_get_public_nonce`, which also returns the signer's public nonce. This + * ensures that the public nonce is not exposed until all commitments have been + * received. + * + * 3. Each individual data struct should be updated with `musig_set_nonce` once a + * nonce is available. This function takes a single signer data struct rather than + * an array because it may fail in the case that the provided nonce does not match + * the commitment. In this case, it is desirable to identify the exact party whose + * nonce was inconsistent. + * + * Fields: + * present: indicates whether the signer's nonce is set + * index: index of the signer in the MuSig key aggregation + * nonce: public nonce, must be a valid curvepoint if the signer is `present` + * nonce_commitment: commitment to the nonce, or all-bits zero if a commitment + * has not yet been set + */ +typedef struct { + int present; + uint32_t index; + secp256k1_pubkey nonce; + unsigned char nonce_commitment[32]; +} secp256k1_musig_session_signer_data; + +/** Opaque data structure that holds a MuSig partial signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is however + * guaranteed to be 32 bytes in size, and can be safely copied/moved. If you need + * to convert to a format suitable for storage, transmission, or comparison, use the + * `musig_partial_signature_serialize` and `musig_partial_signature_parse` + * functions. + */ +typedef struct { + unsigned char data[32]; +} secp256k1_musig_partial_signature; + +/** Computes a combined public key and the hash of the given public keys + * + * Returns: 1 if the public keys were successfully combined, 0 otherwise + * Args: ctx: pointer to a context object initialized for verification + * (cannot be NULL) + * scratch: scratch space used to compute the combined pubkey by + * multiexponentiation. If NULL, an inefficient algorithm is used. + * Out: combined_pk: the MuSig-combined public key (cannot be NULL) + * pk_hash32: if non-NULL, filled with the 32-byte hash of all input public + * keys in order to be used in `musig_session_initialize`. + * In: pubkeys: input array of public keys to combine. The order is important; + * a different order will result in a different combined public + * key (cannot be NULL) + * n_pubkeys: length of pubkeys array + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_pubkey_combine( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + secp256k1_pubkey *combined_pk, + unsigned char *pk_hash32, + const secp256k1_pubkey *pubkeys, + size_t n_pubkeys +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5); + +/** Initializes a signing session for a signer + * + * Returns: 1: session is successfully initialized + * 0: session could not be initialized: secret key or secret nonce overflow + * Args: ctx: pointer to a context object, initialized for signing (cannot + * be NULL) + * Out: session: the session structure to initialize (cannot be NULL) + * signers: an array of signers' data to be initialized. Array length must + * equal to `n_signers` (cannot be NULL) + * nonce_commitment32: filled with a 32-byte commitment to the generated nonce + * (cannot be NULL) + * In: session_id32: a *unique* 32-byte ID to assign to this session (cannot be + * NULL). If a non-unique session_id32 was given then a partial + * signature will LEAK THE SECRET KEY. + * msg32: the 32-byte message to be signed. Shouldn't be NULL unless you + * require sharing public nonces before the message is known + * because it reduces nonce misuse resistance. If NULL, must be + * set with `musig_session_set_msg` before signing and verifying. + * combined_pk: the combined public key of all signers (cannot be NULL) + * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be + * NULL) + * n_signers: length of signers array. Number of signers participating in + * the MuSig. Must be greater than 0 and at most 2^32 - 1. + * my_index: index of this signer in the signers array + * seckey: the signer's 32-byte secret key (cannot be NULL) + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_session_initialize( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + unsigned char *nonce_commitment32, + const unsigned char *session_id32, + const unsigned char *msg32, + const secp256k1_pubkey *combined_pk, + const unsigned char *pk_hash32, + size_t n_signers, + size_t my_index, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(11); + +/** Gets the signer's public nonce given a list of all signers' data with commitments + * + * Returns: 1: public nonce is written in nonce + * 0: signer data is missing commitments or session isn't initialized + * for signing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: the signing session to get the nonce from (cannot be NULL) + * signers: an array of signers' data initialized with + * `musig_session_initialize`. Array length must equal to + * `n_commitments` (cannot be NULL) + * Out: nonce: the nonce (cannot be NULL) + * In: commitments: array of 32-byte nonce commitments (cannot be NULL) + * n_commitments: the length of commitments and signers array. Must be the total + * number of signers participating in the MuSig. + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_get_public_nonce( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + secp256k1_pubkey *nonce, + const unsigned char *const *commitments, + size_t n_commitments +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Initializes a verifier session that can be used for verifying nonce commitments + * and partial signatures. It does not have secret key material and therefore can not + * be used to create signatures. + * + * Returns: 1 when session is successfully initialized, 0 otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: session: the session structure to initialize (cannot be NULL) + * signers: an array of signers' data to be initialized. Array length must + * equal to `n_signers`(cannot be NULL) + * In: msg32: the 32-byte message to be signed If NULL, must be set with + * `musig_session_set_msg` before using the session for verifying + * partial signatures. + * combined_pk: the combined public key of all signers (cannot be NULL) + * pk_hash32: the 32-byte hash of the signers' individual keys (cannot be NULL) + * commitments: array of 32-byte nonce commitments. Array length must equal to + * `n_signers` (cannot be NULL) + * n_signers: length of signers and commitments array. Number of signers + * participating in the MuSig. Must be greater than 0 and at most + * 2^32 - 1. + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_session_initialize_verifier( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + secp256k1_musig_session_signer_data *signers, + const unsigned char *msg32, + const secp256k1_pubkey *combined_pk, + const unsigned char *pk_hash32, + const unsigned char *const *commitments, + size_t n_signers +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Checks a signer's public nonce against a commitment to said nonce, and update + * data structure if they match + * + * Returns: 1: commitment was valid, data structure updated + * 0: commitment was invalid, nothing happened + * Args: ctx: pointer to a context object (cannot be NULL) + * signer: pointer to the signer data to update (cannot be NULL). Must have + * been used with `musig_session_get_public_nonce` or initialized + * with `musig_session_initialize_verifier`. + * In: nonce: signer's alleged public nonce (cannot be NULL) + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_set_nonce( + const secp256k1_context* ctx, + secp256k1_musig_session_signer_data *signer, + const secp256k1_pubkey *nonce +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Updates a session with the combined public nonce of all signers. The combined + * public nonce is the sum of every signer's public nonce. + * + * Returns: 1: nonces are successfully combined + * 0: a signer's nonce is missing + * Args: ctx: pointer to a context object (cannot be NULL) + * session: session to update with the combined public nonce (cannot be + * NULL) + * signers: an array of signers' data, which must have had public nonces + * set with `musig_set_nonce`. Array length must equal to `n_signers` + * (cannot be NULL) + * n_signers: the length of the signers array. Must be the total number of + * signers participating in the MuSig. + * Out: nonce_is_negated: a pointer to an integer that indicates if the combined + * public nonce had to be negated. + * adaptor: point to add to the combined public nonce. If NULL, nothing is + * added to the combined nonce. + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_session_combine_nonces( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + const secp256k1_musig_session_signer_data *signers, + size_t n_signers, + int *nonce_is_negated, + const secp256k1_pubkey *adaptor +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Sets the message of a session if previously unset + * + * Returns 1 if the message was not set yet and is now successfully set + * 0 otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * session: the session structure to update with the message (cannot be NULL) + * In: msg32: the 32-byte message to be signed (cannot be NULL) + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_session_set_msg( + const secp256k1_context* ctx, + secp256k1_musig_session *session, + const unsigned char *msg32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a MuSig partial signature or adaptor signature + * + * Returns: 1 when the signature could be serialized, 0 otherwise + * Args: ctx: a secp256k1 context object + * Out: out32: pointer to a 32-byte array to store the serialized signature + * In: sig: pointer to the signature + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_partial_signature_serialize( + const secp256k1_context* ctx, + unsigned char *out32, + const secp256k1_musig_partial_signature* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse and verify a MuSig partial signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to a signature object + * In: in32: pointer to the 32-byte signature to be parsed + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature verification with it is + * guaranteed to fail for every message and public key. + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_partial_signature_parse( + const secp256k1_context* ctx, + secp256k1_musig_partial_signature* sig, + const unsigned char *in32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Produces a partial signature + * + * Returns: 1: partial signature constructed + * 0: session in incorrect or inconsistent state + * Args: ctx: pointer to a context object (cannot be NULL) + * session: active signing session for which the combined nonce has been + * computed (cannot be NULL) + * Out: partial_sig: partial signature (cannot be NULL) + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_partial_sign( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + secp256k1_musig_partial_signature *partial_sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Checks that an individual partial signature verifies + * + * This function is essential when using protocols with adaptor signatures. + * However, it is not essential for regular MuSig's, in the sense that if any + * partial signatures does not verify, the full signature will also not verify, so the + * problem will be caught. But this function allows determining the specific party + * who produced an invalid signature, so that signing can be restarted without them. + * + * Returns: 1: partial signature verifies + * 0: invalid signature or bad data + * Args: ctx: pointer to a context object (cannot be NULL) + * session: active session for which the combined nonce has been computed + * (cannot be NULL) + * signer: data for the signer who produced this signature (cannot be NULL) + * In: partial_sig: signature to verify (cannot be NULL) + * pubkey: public key of the signer who produced the signature (cannot be NULL) + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_verify( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + const secp256k1_musig_session_signer_data *signer, + const secp256k1_musig_partial_signature *partial_sig, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Combines partial signatures + * + * Returns: 1: all partial signatures have values in range. Does NOT mean the + * resulting signature verifies. + * 0: some partial signature had s/r out of range + * Args: ctx: pointer to a context object (cannot be NULL) + * session: initialized session for which the combined nonce has been + * computed (cannot be NULL) + * Out: sig: complete signature (cannot be NULL) + * In: partial_sigs: array of partial signatures to combine (cannot be NULL) + * n_sigs: number of signatures in the partial_sigs array + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_partial_sig_combine( + const secp256k1_context* ctx, + const secp256k1_musig_session *session, + secp256k1_schnorrsig *sig, + const secp256k1_musig_partial_signature *partial_sigs, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Converts a partial signature to an adaptor signature by adding a given secret + * adaptor. + * + * Returns: 1: signature and secret adaptor contained valid values + * 0: otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out: adaptor_sig: adaptor signature to produce (cannot be NULL) + * In: partial_sig: partial signature to tweak with secret adaptor (cannot be NULL) + * sec_adaptor32: 32-byte secret adaptor to add to the partial signature (cannot + * be NULL) + * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + int secp256k1_musig_partial_sig_adapt( + const secp256k1_context* ctx, + secp256k1_musig_partial_signature *adaptor_sig, + const secp256k1_musig_partial_signature *partial_sig, + const unsigned char *sec_adaptor32, + int nonce_is_negated +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Extracts a secret adaptor from a MuSig, given all parties' partial + * signatures. This function will not fail unless given grossly invalid data; if it + * is merely given signatures that do not verify, the returned value will be + * nonsense. It is therefore important that all data be verified at earlier steps of + * any protocol that uses this function. + * + * Returns: 1: signatures contained valid data such that an adaptor could be extracted + * 0: otherwise + * Args: ctx: pointer to a context object (cannot be NULL) + * Out:sec_adaptor32: 32-byte secret adaptor (cannot be NULL) + * In: sig: complete 2-of-2 signature (cannot be NULL) + * partial_sigs: array of partial signatures (cannot be NULL) + * n_partial_sigs: number of elements in partial_sigs array + * nonce_is_negated: the `nonce_is_negated` output of `musig_session_combine_nonces` + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif + SECP256K1_WARN_UNUSED_RESULT int secp256k1_musig_extract_secret_adaptor( + const secp256k1_context* ctx, + unsigned char *sec_adaptor32, + const secp256k1_schnorrsig *sig, + const secp256k1_musig_partial_signature *partial_sigs, + size_t n_partial_sigs, + int nonce_is_negated +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +#endif + diff --git a/src/secp256k1/include/secp256k1_schnorrsig.h b/src/secp256k1/include/secp256k1_schnorrsig.h new file mode 100644 index 000000000..e4d02f34f --- /dev/null +++ b/src/secp256k1/include/secp256k1_schnorrsig.h @@ -0,0 +1,129 @@ +#ifndef SECP256K1_SCHNORRSIG_H +#define SECP256K1_SCHNORRSIG_H + +/** This module implements a variant of Schnorr signatures compliant with + * BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki). + */ + +/** Opaque data structure that holds a parsed Schnorr signature. + * + * The exact representation of data inside is implementation defined and not + * guaranteed to be portable between different platforms or versions. It is + * however guaranteed to be 64 bytes in size, and can be safely copied/moved. + * If you need to convert to a format suitable for storage, transmission, or + * comparison, use the `secp256k1_schnorrsig_serialize` and + * `secp256k1_schnorrsig_parse` functions. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_schnorrsig; + +/** Serialize a Schnorr signature. + * + * Returns: 1 + * Args: ctx: a secp256k1 context object + * Out: out64: pointer to a 64-byte array to store the serialized signature + * In: sig: pointer to the signature + * + * See secp256k1_schnorrsig_parse for details about the encoding. + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif +int secp256k1_schnorrsig_serialize( + const secp256k1_context* ctx, + unsigned char *out64, + const secp256k1_schnorrsig* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a Schnorr signature. + * + * Returns: 1 when the signature could be parsed, 0 otherwise. + * Args: ctx: a secp256k1 context object + * Out: sig: pointer to a signature object + * In: in64: pointer to the 64-byte signature to be parsed + * + * The signature is serialized in the form R||s, where R is a 32-byte public + * key (x-coordinate only; the y-coordinate is considered to be the unique + * y-coordinate satisfying the curve equation that is a quadratic residue) + * and s is a 32-byte big-endian scalar. + * + * After the call, sig will always be initialized. If parsing failed or the + * encoded numbers are out of range, signature validation with it is + * guaranteed to fail for every message and public key. + */ +#ifdef __cplusplus +extern "C" +#else +SECP256K1_API +#endif +int secp256k1_schnorrsig_parse( + const secp256k1_context* ctx, + secp256k1_schnorrsig* sig, + const unsigned char *in64 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Create a Schnorr signature. + * + * Returns 1 on success, 0 on failure. + * Args: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * Out: sig: pointer to the returned signature (cannot be NULL) + * nonce_is_negated: a pointer to an integer indicates if signing algorithm negated the + * nonce (can be NULL) + * In: msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp: pointer to a nonce generation function. If NULL, secp256k1_nonce_function_bipschnorr is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + */ +SECP256K1_API int secp256k1_schnorrsig_sign( + const secp256k1_context* ctx, + secp256k1_schnorrsig *sig, + int *nonce_is_negated, + const unsigned char *msg32, + const unsigned char *seckey, + secp256k1_nonce_function noncefp, + void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a Schnorr signature. + * + * Returns: 1: correct signature + * 0: incorrect or unparseable signature + * Args: ctx: a secp256k1 context object, initialized for verification. + * In: sig: the signature being verified (cannot be NULL) + * msg32: the 32-byte message hash being verified (cannot be NULL) + * pubkey: pointer to a public key to verify with (cannot be NULL) + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify( + const secp256k1_context* ctx, + const secp256k1_schnorrsig *sig, + const unsigned char *msg32, + const secp256k1_pubkey *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verifies a set of Schnorr signatures. + * + * Returns 1 if all succeeded, 0 otherwise. In particular, returns 1 if n_sigs is 0. + * + * Args: ctx: a secp256k1 context object, initialized for verification. + * scratch: scratch space used for the multiexponentiation + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays. Must be smaller than + * 2^31 and smaller than half the maximum size_t value. Must be 0 + * if above arrays are NULL. + */ +SECP256K1_API SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorrsig_verify_batch( + const secp256k1_context* ctx, + secp256k1_scratch_space *scratch, + const secp256k1_schnorrsig *const *sig, + const unsigned char *const *msg32, + const secp256k1_pubkey *const *pk, + size_t n_sigs +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); +#endif + diff --git a/src/secp256k1/src/ecmult.h b/src/secp256k1/src/ecmult.h index 6d44aba60..3ed2e435a 100644 --- a/src/secp256k1/src/ecmult.h +++ b/src/secp256k1/src/ecmult.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2013, 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -29,3 +31,60 @@ static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); #endif /* SECP256K1_ECMULT_H */ + +#else +/********************************************************************** + * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_H +#define SECP256K1_ECMULT_H + +#include "num.h" +#include "group.h" +#include "scalar.h" +#include "scratch.h" + +typedef struct { + /* For accelerating the computation of a*P + b*G: */ + secp256k1_ge_storage (*pre_g)[]; /* odd multiples of the generator */ +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage (*pre_g_128)[]; /* odd multiples of 2^128*generator */ +#endif +} secp256k1_ecmult_context; + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx); +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb); +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx); +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx); + +/** Double multiply: R = na*A + ng*G */ +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng); + +typedef int (secp256k1_ecmult_multi_callback)(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data); + +/** + * Multi-multiply: R = inp_g_sc * G + sum_i ni * Ai. + * Chooses the right algorithm for a given number of points and scratch space + * size. Resets and overwrites the given scratch space. If the points do not + * fit in the scratch space the algorithm is repeatedly run with batches of + * points. If no scratch space is given then a simple algorithm is used that + * simply multiplies the points with the corresponding scalars and adds them up. + * Returns: 1 on success (including when inp_g_sc is NULL and n is 0) + * 0 if there is not enough scratch space for a single point or + * callback returns 0 + */ + +#ifdef __cplusplus +extern "C" +#endif +int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n); + +#endif /* SECP256K1_ECMULT_H */ + +#endif + diff --git a/src/secp256k1/src/ecmult_const.h b/src/secp256k1/src/ecmult_const.h index 72bf7d758..0d32fa389 100644 --- a/src/secp256k1/src/ecmult_const.h +++ b/src/secp256k1/src/ecmult_const.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2015 Andrew Poelstra * * Distributed under the MIT software license, see the accompanying * @@ -13,3 +15,24 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q); #endif /* SECP256K1_ECMULT_CONST_H */ + +#else +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_CONST_H +#define SECP256K1_ECMULT_CONST_H + +#include "scalar.h" +#include "group.h" + +/* Here `bits` should be set to the maximum bitlength of the _absolute value_ of `q`, plus + * one because we internally sometimes add 2 to the number during the WNAF conversion. */ +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *q, int bits); + +#endif /* SECP256K1_ECMULT_CONST_H */ +#endif + diff --git a/src/secp256k1/src/ecmult_const_impl.h b/src/secp256k1/src/ecmult_const_impl.h index 7d7a172b7..28636290d 100644 --- a/src/secp256k1/src/ecmult_const_impl.h +++ b/src/secp256k1/src/ecmult_const_impl.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * * Distributed under the MIT software license, see the accompanying * @@ -12,6 +14,7 @@ #include "ecmult_const.h" #include "ecmult_impl.h" + #ifdef USE_ENDOMORPHISM #define WNAF_BITS 128 #else @@ -238,3 +241,273 @@ static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, cons } #endif /* SECP256K1_ECMULT_CONST_IMPL_H */ + +#else +/********************************************************************** + * Copyright (c) 2015 Pieter Wuille, Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_ECMULT_CONST_IMPL_H +#define SECP256K1_ECMULT_CONST_IMPL_H + +#include "scalar.h" +#include "group.h" +#include "ecmult_const.h" +#include "ecmult_impl.h" + +#ifdef USE_ENDOMORPHISM +#define WNAF_BITS 128 +#else +#define WNAF_BITS 256 +#endif +#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w)) +#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w) + +/* This is like `ECMULT_TABLE_GET_GE` but is constant time */ +#define ECMULT_CONST_TABLE_GET_GE(r,pre,n,w) do { \ +int m; \ +int abs_n = (n) * (((n) > 0) * 2 - 1); \ +int idx_n = abs_n / 2; \ +secp256k1_fe neg_y; \ +VERIFY_CHECK(((n) & 1) == 1); \ +VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ +VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ +VERIFY_SETUP(secp256k1_fe_clear(&(r)->x)); \ +VERIFY_SETUP(secp256k1_fe_clear(&(r)->y)); \ +for (m = 0; m < ECMULT_TABLE_SIZE(w); m++) { \ +/* This loop is used to avoid secret data in array indices. See +* the comment in ecmult_gen_impl.h for rationale. */ \ +secp256k1_fe_cmov(&(r)->x, &(pre)[m].x, m == idx_n); \ +secp256k1_fe_cmov(&(r)->y, &(pre)[m].y, m == idx_n); \ +} \ +(r)->infinity = 0; \ +secp256k1_fe_negate(&neg_y, &(r)->y, 1); \ +secp256k1_fe_cmov(&(r)->y, &neg_y, (n) != abs_n); \ +} while(0) + + +/** Convert a number to WNAF notation. + * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. + * It has the following guarantees: + * - each wnaf[i] an odd integer between -(1 << w) and (1 << w) + * - each wnaf[i] is nonzero + * - the number of words set is always WNAF_SIZE(w) + 1 + * + * Adapted from `The Width-w NAF Method Provides Small Memory and Fast Elliptic Scalar + * Multiplications Secure against Side Channel Attacks`, Okeya and Tagaki. M. Joye (Ed.) + * CT-RSA 2003, LNCS 2612, pp. 328-443, 2003. Springer-Verlagy Berlin Heidelberg 2003 + * + * Numbers reference steps of `Algorithm SPA-resistant Width-w NAF with Odd Scalar` on pp. 335 + */ +static int secp256k1_wnaf_const(int *wnaf, secp256k1_scalar s, int w, int size) { + int global_sign; + int skew = 0; + int word = 0; + + /* 1 2 3 */ + int u_last; + int u; + + int flip; + int bit; + secp256k1_scalar neg_s; + int not_neg_one; + /* Note that we cannot handle even numbers by negating them to be odd, as is + * done in other implementations, since if our scalars were specified to have + * width < 256 for performance reasons, their negations would have width 256 + * and we'd lose any performance benefit. Instead, we use a technique from + * Section 4.2 of the Okeya/Tagaki paper, which is to add either 1 (for even) + * or 2 (for odd) to the number we are encoding, returning a skew value indicating + * this, and having the caller compensate after doing the multiplication. + * + * In fact, we _do_ want to negate numbers to minimize their bit-lengths (and in + * particular, to ensure that the outputs from the endomorphism-split fit into + * 128 bits). If we negate, the parity of our number flips, inverting which of + * {1, 2} we want to add to the scalar when ensuring that it's odd. Further + * complicating things, -1 interacts badly with `secp256k1_scalar_cadd_bit` and + * we need to special-case it in this logic. */ + flip = secp256k1_scalar_is_high(&s); + /* We add 1 to even numbers, 2 to odd ones, noting that negation flips parity */ + bit = flip ^ !secp256k1_scalar_is_even(&s); + /* We check for negative one, since adding 2 to it will cause an overflow */ + secp256k1_scalar_negate(&neg_s, &s); + not_neg_one = !secp256k1_scalar_is_one(&neg_s); + secp256k1_scalar_cadd_bit(&s, bit, not_neg_one); + /* If we had negative one, flip == 1, s.d[0] == 0, bit == 1, so caller expects + * that we added two to it and flipped it. In fact for -1 these operations are + * identical. We only flipped, but since skewing is required (in the sense that + * the skew must be 1 or 2, never zero) and flipping is not, we need to change + * our flags to claim that we only skewed. */ + global_sign = secp256k1_scalar_cond_negate(&s, flip); + global_sign *= not_neg_one * 2 - 1; + skew = 1 << bit; + + /* 4 */ + u_last = secp256k1_scalar_shr_int(&s, w); + while (word * w < size) { + int sign; + int even; + + /* 4.1 4.4 */ + u = secp256k1_scalar_shr_int(&s, w); + /* 4.2 */ + even = ((u & 1) == 0); + sign = 2 * (u_last > 0) - 1; + u += sign * even; + u_last -= sign * even * (1 << w); + + /* 4.3, adapted for global sign change */ + wnaf[word++] = u_last * global_sign; + + u_last = u; + } + wnaf[word] = u * global_sign; + + VERIFY_CHECK(secp256k1_scalar_is_zero(&s)); + VERIFY_CHECK(word == WNAF_SIZE_BITS(size, w)); + return skew; +} + +static void secp256k1_ecmult_const(secp256k1_gej *r, const secp256k1_ge *a, const secp256k1_scalar *scalar, int size) { + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge tmpa; + secp256k1_fe Z; + + int skew_1; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; + int wnaf_lam[1 + WNAF_SIZE(WINDOW_A - 1)]; + int skew_lam; + secp256k1_scalar q_1, q_lam; +#endif + int wnaf_1[1 + WNAF_SIZE(WINDOW_A - 1)]; + + int i; + secp256k1_scalar sc = *scalar; + + /* build wnaf representation for q. */ + int rsize = size; +#ifdef USE_ENDOMORPHISM + if (size > 128) { + rsize = 128; + /* split q into q_1 and q_lam (where q = q_1 + q_lam*lambda, and q_1 and q_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&q_1, &q_lam, &sc); + skew_1 = secp256k1_wnaf_const(wnaf_1, q_1, WINDOW_A - 1, 128); + skew_lam = secp256k1_wnaf_const(wnaf_lam, q_lam, WINDOW_A - 1, 128); + } else +#endif + { + skew_1 = secp256k1_wnaf_const(wnaf_1, sc, WINDOW_A - 1, size); +#ifdef USE_ENDOMORPHISM + skew_lam = 0; +#endif + } + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + */ + secp256k1_gej_set_ge(r, a); + secp256k1_ecmult_odd_multiples_table_globalz_windowa(pre_a, &Z, r); + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_fe_normalize_weak(&pre_a[i].y); + } +#ifdef USE_ENDOMORPHISM + if (size > 128) { + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&pre_a_lam[i], &pre_a[i]); + } + } +#endif + + /* first loop iteration (separated out so we can directly set r, rather + * than having it start at infinity, get doubled several times, then have + * its new value added to it) */ + i = wnaf_1[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, i, WINDOW_A); + secp256k1_gej_set_ge(r, &tmpa); +#ifdef USE_ENDOMORPHISM + if (size > 128) { + i = wnaf_lam[WNAF_SIZE_BITS(rsize, WINDOW_A - 1)]; + VERIFY_CHECK(i != 0); + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, i, WINDOW_A); + secp256k1_gej_add_ge(r, r, &tmpa); + } +#endif + /* remaining loop iterations */ + for (i = WNAF_SIZE_BITS(rsize, WINDOW_A - 1) - 1; i >= 0; i--) { + int n; + int j; + for (j = 0; j < WINDOW_A - 1; ++j) { + secp256k1_gej_double_nonzero(r, r, NULL); + } + + n = wnaf_1[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); +#ifdef USE_ENDOMORPHISM + if (size > 128) { + n = wnaf_lam[i]; + ECMULT_CONST_TABLE_GET_GE(&tmpa, pre_a_lam, n, WINDOW_A); + VERIFY_CHECK(n != 0); + secp256k1_gej_add_ge(r, r, &tmpa); + } +#endif + } + + secp256k1_fe_mul(&r->z, &r->z, &Z); + + { + /* Correct for wNAF skew */ + secp256k1_ge correction = *a; + secp256k1_ge_storage correction_1_stor; +#ifdef USE_ENDOMORPHISM + secp256k1_ge_storage correction_lam_stor; +#endif + secp256k1_ge_storage a2_stor; + secp256k1_gej tmpj; + secp256k1_gej_set_ge(&tmpj, &correction); + secp256k1_gej_double_var(&tmpj, &tmpj, NULL); + secp256k1_ge_set_gej(&correction, &tmpj); + secp256k1_ge_to_storage(&correction_1_stor, a); +#ifdef USE_ENDOMORPHISM + if (size > 128) { + secp256k1_ge_to_storage(&correction_lam_stor, a); + } +#endif + secp256k1_ge_to_storage(&a2_stor, &correction); + + /* For odd numbers this is 2a (so replace it), for even ones a (so no-op) */ + secp256k1_ge_storage_cmov(&correction_1_stor, &a2_stor, skew_1 == 2); +#ifdef USE_ENDOMORPHISM + if (size > 128) { + secp256k1_ge_storage_cmov(&correction_lam_stor, &a2_stor, skew_lam == 2); + } +#endif + + /* Apply the correction */ + secp256k1_ge_from_storage(&correction, &correction_1_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); + +#ifdef USE_ENDOMORPHISM + if (size > 128) { + secp256k1_ge_from_storage(&correction, &correction_lam_stor); + secp256k1_ge_neg(&correction, &correction); + secp256k1_ge_mul_lambda(&correction, &correction); + secp256k1_gej_add_ge(r, r, &correction); + } +#endif + } +} + +#endif /* SECP256K1_ECMULT_CONST_IMPL_H */ + +#endif + diff --git a/src/secp256k1/src/ecmult_impl.h b/src/secp256k1/src/ecmult_impl.h index 93d3794cb..b761304cf 100644 --- a/src/secp256k1/src/ecmult_impl.h +++ b/src/secp256k1/src/ecmult_impl.h @@ -1,3 +1,6 @@ +#ifndef ENABLE_MODULE_MUSIG + + /********************************************************************** * Copyright (c) 2013, 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -404,3 +407,1176 @@ static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej } #endif /* SECP256K1_ECMULT_IMPL_H */ + +#else + +/***************************************************************************** + * Copyright (c) 2013, 2014, 2017 Pieter Wuille, Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php. * + *****************************************************************************/ + +#ifndef SECP256K1_ECMULT_IMPL_H +#define SECP256K1_ECMULT_IMPL_H + +#include +#include + +#include "group.h" +#include "scalar.h" +#include "ecmult.h" + +#if defined(EXHAUSTIVE_TEST_ORDER) +/* We need to lower these values for exhaustive tests because + * the tables cannot have infinities in them (this breaks the + * affine-isomorphism stuff which tracks z-ratios) */ +# if EXHAUSTIVE_TEST_ORDER > 128 +# define WINDOW_A 5 +# define WINDOW_G 8 +# elif EXHAUSTIVE_TEST_ORDER > 8 +# define WINDOW_A 4 +# define WINDOW_G 4 +# else +# define WINDOW_A 2 +# define WINDOW_G 2 +# endif +#else +/* optimal for 128-bit and 256-bit exponents. */ +#define WINDOW_A 5 +/** larger numbers may result in slightly better performance, at the cost of + exponentially larger precomputed tables. */ +#ifdef USE_ENDOMORPHISM +/** Two tables for window size 15: 1.375 MiB. */ +#define WINDOW_G 15 +#else +/** One table for window size 16: 1.375 MiB. */ +#define WINDOW_G 16 +#endif +#endif + +#ifdef USE_ENDOMORPHISM +#define WNAF_BITS 128 +#else +#define WNAF_BITS 256 +#endif +#define WNAF_SIZE_BITS(bits, w) (((bits) + (w) - 1) / (w)) +#define WNAF_SIZE(w) WNAF_SIZE_BITS(WNAF_BITS, w) + +/** The number of entries a table with precomputed multiples needs to have. */ +#define ECMULT_TABLE_SIZE(w) (1 << ((w)-2)) + +/* The number of objects allocated on the scratch space for ecmult_multi algorithms */ +#define PIPPENGER_SCRATCH_OBJECTS 6 +#define STRAUSS_SCRATCH_OBJECTS 6 + +#define PIPPENGER_MAX_BUCKET_WINDOW 12 + +/* Minimum number of points for which pippenger_wnaf is faster than strauss wnaf */ +#ifdef USE_ENDOMORPHISM +#define ECMULT_PIPPENGER_THRESHOLD 88 +#else +#define ECMULT_PIPPENGER_THRESHOLD 160 +#endif + +#ifdef USE_ENDOMORPHISM +#define ECMULT_MAX_POINTS_PER_BATCH 5000000 +#else +#define ECMULT_MAX_POINTS_PER_BATCH 10000000 +#endif + +/** Fill a table 'prej' with precomputed odd multiples of a. Prej will contain + * the values [1*a,3*a,...,(2*n-1)*a], so it space for n values. zr[0] will + * contain prej[0].z / a.z. The other zr[i] values = prej[i].z / prej[i-1].z. + * Prej's Z values are undefined, except for the last value. + */ +static void secp256k1_ecmult_odd_multiples_table(int n, secp256k1_gej *prej, secp256k1_fe *zr, const secp256k1_gej *a) { + secp256k1_gej d; + secp256k1_ge a_ge, d_ge; + int i; + + VERIFY_CHECK(!a->infinity); + + secp256k1_gej_double_var(&d, a, NULL); + + /* + * Perform the additions on an isomorphism where 'd' is affine: drop the z coordinate + * of 'd', and scale the 1P starting value's x/y coordinates without changing its z. + */ + d_ge.x = d.x; + d_ge.y = d.y; + d_ge.infinity = 0; + + secp256k1_ge_set_gej_zinv(&a_ge, a, &d.z); + prej[0].x = a_ge.x; + prej[0].y = a_ge.y; + prej[0].z = a->z; + prej[0].infinity = 0; + + zr[0] = d.z; + for (i = 1; i < n; i++) { + secp256k1_gej_add_ge_var(&prej[i], &prej[i-1], &d_ge, &zr[i]); + } + + /* + * Each point in 'prej' has a z coordinate too small by a factor of 'd.z'. Only + * the final point's z coordinate is actually used though, so just update that. + */ + secp256k1_fe_mul(&prej[n-1].z, &prej[n-1].z, &d.z); +} + +/** Fill a table 'pre' with precomputed odd multiples of a. + * + * There are two versions of this function: + * - secp256k1_ecmult_odd_multiples_table_globalz_windowa which brings its + * resulting point set to a single constant Z denominator, stores the X and Y + * coordinates as ge_storage points in pre, and stores the global Z in rz. + * It only operates on tables sized for WINDOW_A wnaf multiples. + * - secp256k1_ecmult_odd_multiples_table_storage_var, which converts its + * resulting point set to actually affine points, and stores those in pre. + * It operates on tables of any size, but uses heap-allocated temporaries. + * + * To compute a*P + b*G, we compute a table for P using the first function, + * and for G using the second (which requires an inverse, but it only needs to + * happen once). + */ +static void secp256k1_ecmult_odd_multiples_table_globalz_windowa(secp256k1_ge *pre, secp256k1_fe *globalz, const secp256k1_gej *a) { + secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; + + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), prej, zr, a); + /* Bring them to the same Z denominator. */ + secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A), pre, globalz, prej, zr); +} + +static void secp256k1_ecmult_odd_multiples_table_storage_var(const int n, secp256k1_ge_storage *pre, const secp256k1_gej *a) { + secp256k1_gej d; + secp256k1_ge d_ge, p_ge; + secp256k1_gej pj; + secp256k1_fe zi; + secp256k1_fe zr; + secp256k1_fe dx_over_dz_squared; + int i; + + VERIFY_CHECK(!a->infinity); + + secp256k1_gej_double_var(&d, a, NULL); + + /* First, we perform all the additions in an isomorphic curve obtained by multiplying + * all `z` coordinates by 1/`d.z`. In these coordinates `d` is affine so we can use + * `secp256k1_gej_add_ge_var` to perform the additions. For each addition, we store + * the resulting y-coordinate and the z-ratio, since we only have enough memory to + * store two field elements. These are sufficient to efficiently undo the isomorphism + * and recompute all the `x`s. + */ + d_ge.x = d.x; + d_ge.y = d.y; + d_ge.infinity = 0; + + secp256k1_ge_set_gej_zinv(&p_ge, a, &d.z); + pj.x = p_ge.x; + pj.y = p_ge.y; + pj.z = a->z; + pj.infinity = 0; + + for (i = 0; i < (n - 1); i++) { + secp256k1_fe_normalize_var(&pj.y); + secp256k1_fe_to_storage(&pre[i].y, &pj.y); + secp256k1_gej_add_ge_var(&pj, &pj, &d_ge, &zr); + secp256k1_fe_normalize_var(&zr); + secp256k1_fe_to_storage(&pre[i].x, &zr); + } + + /* Invert d.z in the same batch, preserving pj.z so we can extract 1/d.z */ + secp256k1_fe_mul(&zi, &pj.z, &d.z); + secp256k1_fe_inv_var(&zi, &zi); + + /* Directly set `pre[n - 1]` to `pj`, saving the inverted z-coordinate so + * that we can combine it with the saved z-ratios to compute the other zs + * without any more inversions. */ + secp256k1_ge_set_gej_zinv(&p_ge, &pj, &zi); + secp256k1_ge_to_storage(&pre[n - 1], &p_ge); + + /* Compute the actual x-coordinate of D, which will be needed below. */ + secp256k1_fe_mul(&d.z, &zi, &pj.z); /* d.z = 1/d.z */ + secp256k1_fe_sqr(&dx_over_dz_squared, &d.z); + secp256k1_fe_mul(&dx_over_dz_squared, &dx_over_dz_squared, &d.x); + + /* Going into the second loop, we have set `pre[n-1]` to its final affine + * form, but still need to set `pre[i]` for `i` in 0 through `n-2`. We + * have `zi = (p.z * d.z)^-1`, where + * + * `p.z` is the z-coordinate of the point on the isomorphic curve + * which was ultimately assigned to `pre[n-1]`. + * `d.z` is the multiplier that must be applied to all z-coordinates + * to move from our isomorphic curve back to secp256k1; so the + * product `p.z * d.z` is the z-coordinate of the secp256k1 + * point assigned to `pre[n-1]`. + * + * All subsequent inverse-z-coordinates can be obtained by multiplying this + * factor by successive z-ratios, which is much more efficient than directly + * computing each one. + * + * Importantly, these inverse-zs will be coordinates of points on secp256k1, + * while our other stored values come from computations on the isomorphic + * curve. So in the below loop, we will take care not to actually use `zi` + * or any derived values until we're back on secp256k1. + */ + i = n - 1; + while (i > 0) { + secp256k1_fe zi2, zi3; + const secp256k1_fe *rzr; + i--; + + secp256k1_ge_from_storage(&p_ge, &pre[i]); + + /* For each remaining point, we extract the z-ratio from the stored + * x-coordinate, compute its z^-1 from that, and compute the full + * point from that. */ + rzr = &p_ge.x; + secp256k1_fe_mul(&zi, &zi, rzr); + secp256k1_fe_sqr(&zi2, &zi); + secp256k1_fe_mul(&zi3, &zi2, &zi); + /* To compute the actual x-coordinate, we use the stored z ratio and + * y-coordinate, which we obtained from `secp256k1_gej_add_ge_var` + * in the loop above, as well as the inverse of the square of its + * z-coordinate. We store the latter in the `zi2` variable, which is + * computed iteratively starting from the overall Z inverse then + * multiplying by each z-ratio in turn. + * + * Denoting the z-ratio as `rzr`, we observe that it is equal to `h` + * from the inside of the above `gej_add_ge_var` call. This satisfies + * + * rzr = d_x * z^2 - x * d_z^2 + * + * where (`d_x`, `d_z`) are Jacobian coordinates of `D` and `(x, z)` + * are Jacobian coordinates of our desired point -- except both are on + * the isomorphic curve that we were using when we called `gej_add_ge_var`. + * To get back to secp256k1, we must multiply both `z`s by `d_z`, or + * equivalently divide both `x`s by `d_z^2`. Our equation then becomes + * + * rzr = d_x * z^2 / d_z^2 - x + * + * (The left-hand-side, being a ratio of z-coordinates, is unaffected + * by the isomorphism.) + * + * Rearranging to solve for `x`, we have + * + * x = d_x * z^2 / d_z^2 - rzr + * + * But what we actually want is the affine coordinate `X = x/z^2`, + * which will satisfy + * + * X = d_x / d_z^2 - rzr / z^2 + * = dx_over_dz_squared - rzr * zi2 + */ + secp256k1_fe_mul(&p_ge.x, rzr, &zi2); + secp256k1_fe_negate(&p_ge.x, &p_ge.x, 1); + secp256k1_fe_add(&p_ge.x, &dx_over_dz_squared); + /* y is stored_y/z^3, as we expect */ + secp256k1_fe_mul(&p_ge.y, &p_ge.y, &zi3); + /* Store */ + secp256k1_ge_to_storage(&pre[i], &p_ge); + } +} + +/** The following two macro retrieves a particular odd multiple from a table + * of precomputed multiples. */ +#define ECMULT_TABLE_GET_GE(r,pre,n,w) do { \ +VERIFY_CHECK(((n) & 1) == 1); \ +VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ +VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ +if ((n) > 0) { \ +*(r) = (pre)[((n)-1)/2]; \ +} else { \ +secp256k1_ge_neg((r), &(pre)[(-(n)-1)/2]); \ +} \ +} while(0) + +#define ECMULT_TABLE_GET_GE_STORAGE(r,pre,n,w) do { \ +VERIFY_CHECK(((n) & 1) == 1); \ +VERIFY_CHECK((n) >= -((1 << ((w)-1)) - 1)); \ +VERIFY_CHECK((n) <= ((1 << ((w)-1)) - 1)); \ +if ((n) > 0) { \ +secp256k1_ge_from_storage((r), &(pre)[((n)-1)/2]); \ +} else { \ +secp256k1_ge_from_storage((r), &(pre)[(-(n)-1)/2]); \ +secp256k1_ge_neg((r), (r)); \ +} \ +} while(0) + +static void secp256k1_ecmult_context_init(secp256k1_ecmult_context *ctx) { + ctx->pre_g = NULL; +#ifdef USE_ENDOMORPHISM + ctx->pre_g_128 = NULL; +#endif +} + +static void secp256k1_ecmult_context_build(secp256k1_ecmult_context *ctx, const secp256k1_callback *cb) { + secp256k1_gej gj; + + if (ctx->pre_g != NULL) { + return; + } + + /* get the generator */ + secp256k1_gej_set_ge(&gj, &secp256k1_ge_const_g); + + ctx->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* precompute the tables with odd multiples */ + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g, &gj); + +#ifdef USE_ENDOMORPHISM + { + secp256k1_gej g_128j; + int i; + + ctx->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, sizeof((*ctx->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G)); + + /* calculate 2^128*generator */ + g_128j = gj; + for (i = 0; i < 128; i++) { + secp256k1_gej_double_var(&g_128j, &g_128j, NULL); + } + secp256k1_ecmult_odd_multiples_table_storage_var(ECMULT_TABLE_SIZE(WINDOW_G), *ctx->pre_g_128, &g_128j); + } +#endif +} + +static void secp256k1_ecmult_context_clone(secp256k1_ecmult_context *dst, + const secp256k1_ecmult_context *src, const secp256k1_callback *cb) { + if (src->pre_g == NULL) { + dst->pre_g = NULL; + } else { + size_t size = sizeof((*dst->pre_g)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g, src->pre_g, size); + } +#ifdef USE_ENDOMORPHISM + if (src->pre_g_128 == NULL) { + dst->pre_g_128 = NULL; + } else { + size_t size = sizeof((*dst->pre_g_128)[0]) * ECMULT_TABLE_SIZE(WINDOW_G); + dst->pre_g_128 = (secp256k1_ge_storage (*)[])checked_malloc(cb, size); + memcpy(dst->pre_g_128, src->pre_g_128, size); + } +#endif +} + +static int secp256k1_ecmult_context_is_built(const secp256k1_ecmult_context *ctx) { + return ctx->pre_g != NULL; +} + +static void secp256k1_ecmult_context_clear(secp256k1_ecmult_context *ctx) { + free(ctx->pre_g); +#ifdef USE_ENDOMORPHISM + free(ctx->pre_g_128); +#endif + secp256k1_ecmult_context_init(ctx); +} + +/** Convert a number to WNAF notation. The number becomes represented by sum(2^i * wnaf[i], i=0..bits), + * with the following guarantees: + * - each wnaf[i] is either 0, or an odd integer between -(1<<(w-1) - 1) and (1<<(w-1) - 1) + * - two non-zero entries in wnaf are separated by at least w-1 zeroes. + * - the number of set values in wnaf is returned. This number is at most 256, and at most one more + * than the number of bits in the (absolute value) of the input. + */ +static int secp256k1_ecmult_wnaf(int *wnaf, int len, const secp256k1_scalar *a, int w) { + secp256k1_scalar s = *a; + int last_set_bit = -1; + int bit = 0; + int sign = 1; + int carry = 0; + + VERIFY_CHECK(wnaf != NULL); + VERIFY_CHECK(0 <= len && len <= 256); + VERIFY_CHECK(a != NULL); + VERIFY_CHECK(2 <= w && w <= 31); + + memset(wnaf, 0, len * sizeof(wnaf[0])); + + if (secp256k1_scalar_get_bits(&s, 255, 1)) { + secp256k1_scalar_negate(&s, &s); + sign = -1; + } + + while (bit < len) { + int now; + int word; + if (secp256k1_scalar_get_bits(&s, bit, 1) == (unsigned int)carry) { + bit++; + continue; + } + + now = w; + if (now > len - bit) { + now = len - bit; + } + + word = secp256k1_scalar_get_bits_var(&s, bit, now) + carry; + + carry = (word >> (w-1)) & 1; + word -= carry << w; + + wnaf[bit] = sign * word; + last_set_bit = bit; + + bit += now; + } +#ifdef VERIFY + CHECK(carry == 0); + while (bit < 256) { + CHECK(secp256k1_scalar_get_bits(&s, bit++, 1) == 0); + } +#endif + return last_set_bit + 1; +} + +struct secp256k1_strauss_point_state { +#ifdef USE_ENDOMORPHISM + secp256k1_scalar na_1, na_lam; + int wnaf_na_1[130]; + int wnaf_na_lam[130]; + int bits_na_1; + int bits_na_lam; +#else + int wnaf_na[256]; + int bits_na; +#endif + size_t input_pos; +}; + +struct secp256k1_strauss_state { + secp256k1_gej* prej; + secp256k1_fe* zr; + secp256k1_ge* pre_a; +#ifdef USE_ENDOMORPHISM + secp256k1_ge* pre_a_lam; +#endif + struct secp256k1_strauss_point_state* ps; +}; + +static void secp256k1_ecmult_strauss_wnaf(const secp256k1_ecmult_context *ctx, const struct secp256k1_strauss_state *state, secp256k1_gej *r, int num, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_ge tmpa; + secp256k1_fe Z; +#ifdef USE_ENDOMORPHISM + /* Splitted G factors. */ + secp256k1_scalar ng_1, ng_128; + int wnaf_ng_1[129]; + int bits_ng_1 = 0; + int wnaf_ng_128[129]; + int bits_ng_128 = 0; +#else + int wnaf_ng[256]; + int bits_ng = 0; +#endif + int i; + int bits = 0; + int np; + int no = 0; + + for (np = 0; np < num; ++np) { + if (secp256k1_scalar_is_zero(&na[np]) || secp256k1_gej_is_infinity(&a[np])) { + continue; + } + state->ps[no].input_pos = np; +#ifdef USE_ENDOMORPHISM + /* split na into na_1 and na_lam (where na = na_1 + na_lam*lambda, and na_1 and na_lam are ~128 bit) */ + secp256k1_scalar_split_lambda(&state->ps[no].na_1, &state->ps[no].na_lam, &na[np]); + + /* build wnaf representation for na_1 and na_lam. */ + state->ps[no].bits_na_1 = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_1, 130, &state->ps[no].na_1, WINDOW_A); + state->ps[no].bits_na_lam = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na_lam, 130, &state->ps[no].na_lam, WINDOW_A); + VERIFY_CHECK(state->ps[no].bits_na_1 <= 130); + VERIFY_CHECK(state->ps[no].bits_na_lam <= 130); + if (state->ps[no].bits_na_1 > bits) { + bits = state->ps[no].bits_na_1; + } + if (state->ps[no].bits_na_lam > bits) { + bits = state->ps[no].bits_na_lam; + } +#else + /* build wnaf representation for na. */ + state->ps[no].bits_na = secp256k1_ecmult_wnaf(state->ps[no].wnaf_na, 256, &na[np], WINDOW_A); + if (state->ps[no].bits_na > bits) { + bits = state->ps[no].bits_na; + } +#endif + ++no; + } + + /* Calculate odd multiples of a. + * All multiples are brought to the same Z 'denominator', which is stored + * in Z. Due to secp256k1' isomorphism we can do all operations pretending + * that the Z coordinate was 1, use affine addition formulae, and correct + * the Z coordinate of the result once at the end. + * The exception is the precomputed G table points, which are actually + * affine. Compared to the base used for other points, they have a Z ratio + * of 1/Z, so we can use secp256k1_gej_add_zinv_var, which uses the same + * isomorphism to efficiently add with a known Z inverse. + */ + if (no > 0) { + /* Compute the odd multiples in Jacobian form. */ + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej, state->zr, &a[state->ps[0].input_pos]); + for (np = 1; np < no; ++np) { + secp256k1_gej tmp = a[state->ps[np].input_pos]; +#ifdef VERIFY + secp256k1_fe_normalize_var(&(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); +#endif + secp256k1_gej_rescale(&tmp, &(state->prej[(np - 1) * ECMULT_TABLE_SIZE(WINDOW_A) + ECMULT_TABLE_SIZE(WINDOW_A) - 1].z)); + secp256k1_ecmult_odd_multiples_table(ECMULT_TABLE_SIZE(WINDOW_A), state->prej + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &tmp); + secp256k1_fe_mul(state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), state->zr + np * ECMULT_TABLE_SIZE(WINDOW_A), &(a[state->ps[np].input_pos].z)); + } + /* Bring them to the same Z denominator. */ + secp256k1_ge_globalz_set_table_gej(ECMULT_TABLE_SIZE(WINDOW_A) * no, state->pre_a, &Z, state->prej, state->zr); + } else { + secp256k1_fe_set_int(&Z, 1); + } + +#ifdef USE_ENDOMORPHISM + for (np = 0; np < no; ++np) { + for (i = 0; i < ECMULT_TABLE_SIZE(WINDOW_A); i++) { + secp256k1_ge_mul_lambda(&state->pre_a_lam[np * ECMULT_TABLE_SIZE(WINDOW_A) + i], &state->pre_a[np * ECMULT_TABLE_SIZE(WINDOW_A) + i]); + } + } + + if (ng) { + /* split ng into ng_1 and ng_128 (where gn = gn_1 + gn_128*2^128, and gn_1 and gn_128 are ~128 bit) */ + secp256k1_scalar_split_128(&ng_1, &ng_128, ng); + + /* Build wnaf representation for ng_1 and ng_128 */ + bits_ng_1 = secp256k1_ecmult_wnaf(wnaf_ng_1, 129, &ng_1, WINDOW_G); + bits_ng_128 = secp256k1_ecmult_wnaf(wnaf_ng_128, 129, &ng_128, WINDOW_G); + if (bits_ng_1 > bits) { + bits = bits_ng_1; + } + if (bits_ng_128 > bits) { + bits = bits_ng_128; + } + } +#else + if (ng) { + bits_ng = secp256k1_ecmult_wnaf(wnaf_ng, 256, ng, WINDOW_G); + if (bits_ng > bits) { + bits = bits_ng; + } + } +#endif + + secp256k1_gej_set_infinity(r); + + for (i = bits - 1; i >= 0; i--) { + int n; + secp256k1_gej_double_var(r, r, NULL); +#ifdef USE_ENDOMORPHISM + for (np = 0; np < no; ++np) { + if (i < state->ps[np].bits_na_1 && (n = state->ps[np].wnaf_na_1[i])) { + ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + if (i < state->ps[np].bits_na_lam && (n = state->ps[np].wnaf_na_lam[i])) { + ECMULT_TABLE_GET_GE(&tmpa, state->pre_a_lam + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + } + if (i < bits_ng_1 && (n = wnaf_ng_1[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } + if (i < bits_ng_128 && (n = wnaf_ng_128[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g_128, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#else + for (np = 0; np < no; ++np) { + if (i < state->ps[np].bits_na && (n = state->ps[np].wnaf_na[i])) { + ECMULT_TABLE_GET_GE(&tmpa, state->pre_a + np * ECMULT_TABLE_SIZE(WINDOW_A), n, WINDOW_A); + secp256k1_gej_add_ge_var(r, r, &tmpa, NULL); + } + } + if (i < bits_ng && (n = wnaf_ng[i])) { + ECMULT_TABLE_GET_GE_STORAGE(&tmpa, *ctx->pre_g, n, WINDOW_G); + secp256k1_gej_add_zinv_var(r, r, &tmpa, &Z); + } +#endif + } + + if (!r->infinity) { + secp256k1_fe_mul(&r->z, &r->z, &Z); + } +} + +static void secp256k1_ecmult(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_gej *a, const secp256k1_scalar *na, const secp256k1_scalar *ng) { + secp256k1_gej prej[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_fe zr[ECMULT_TABLE_SIZE(WINDOW_A)]; + secp256k1_ge pre_a[ECMULT_TABLE_SIZE(WINDOW_A)]; + struct secp256k1_strauss_point_state ps[1]; +#ifdef USE_ENDOMORPHISM + secp256k1_ge pre_a_lam[ECMULT_TABLE_SIZE(WINDOW_A)]; +#endif + struct secp256k1_strauss_state state; + + state.prej = prej; + state.zr = zr; + state.pre_a = pre_a; +#ifdef USE_ENDOMORPHISM + state.pre_a_lam = pre_a_lam; +#endif + state.ps = ps; + secp256k1_ecmult_strauss_wnaf(ctx, &state, r, 1, a, na, ng); +} + +static size_t secp256k1_strauss_scratch_size(size_t n_points) { +#ifdef USE_ENDOMORPHISM + static const size_t point_size = (2 * sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); +#else + static const size_t point_size = (sizeof(secp256k1_ge) + sizeof(secp256k1_gej) + sizeof(secp256k1_fe)) * ECMULT_TABLE_SIZE(WINDOW_A) + sizeof(struct secp256k1_strauss_point_state) + sizeof(secp256k1_gej) + sizeof(secp256k1_scalar); +#endif + return n_points*point_size; +} + +static int secp256k1_ecmult_strauss_batch(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points, size_t cb_offset) { + secp256k1_gej* points; + secp256k1_scalar* scalars; + struct secp256k1_strauss_state state; + size_t i; + + secp256k1_gej_set_infinity(r); + if (inp_g_sc == NULL && n_points == 0) { + return 1; + } + + if (!secp256k1_scratch_allocate_frame(scratch, secp256k1_strauss_scratch_size(n_points), STRAUSS_SCRATCH_OBJECTS)) { + return 0; + } + points = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_gej)); + scalars = (secp256k1_scalar*)secp256k1_scratch_alloc(scratch, n_points * sizeof(secp256k1_scalar)); + state.prej = (secp256k1_gej*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_gej)); + state.zr = (secp256k1_fe*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_fe)); +#ifdef USE_ENDOMORPHISM + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * 2 * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); + state.pre_a_lam = state.pre_a + n_points * ECMULT_TABLE_SIZE(WINDOW_A); +#else + state.pre_a = (secp256k1_ge*)secp256k1_scratch_alloc(scratch, n_points * ECMULT_TABLE_SIZE(WINDOW_A) * sizeof(secp256k1_ge)); +#endif + state.ps = (struct secp256k1_strauss_point_state*)secp256k1_scratch_alloc(scratch, n_points * sizeof(struct secp256k1_strauss_point_state)); + + for (i = 0; i < n_points; i++) { + secp256k1_ge point; + if (!cb(&scalars[i], &point, i+cb_offset, cbdata)) { + secp256k1_scratch_deallocate_frame(scratch); + return 0; + } + secp256k1_gej_set_ge(&points[i], &point); + } + secp256k1_ecmult_strauss_wnaf(ctx, &state, r, n_points, points, scalars, inp_g_sc); + secp256k1_scratch_deallocate_frame(scratch); + return 1; +} + +/* Wrapper for secp256k1_ecmult_multi_func interface */ +static int secp256k1_ecmult_strauss_batch_single(const secp256k1_ecmult_context *actx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { + return secp256k1_ecmult_strauss_batch(actx, scratch, r, inp_g_sc, cb, cbdata, n, 0); +} + +static size_t secp256k1_strauss_max_points(secp256k1_scratch *scratch) { + return secp256k1_scratch_max_allocation(scratch, STRAUSS_SCRATCH_OBJECTS) / secp256k1_strauss_scratch_size(1); +} + +/** Convert a number to WNAF notation. + * The number becomes represented by sum(2^{wi} * wnaf[i], i=0..WNAF_SIZE(w)+1) - return_val. + * It has the following guarantees: + * - each wnaf[i] is either 0 or an odd integer between -(1 << w) and (1 << w) + * - the number of words set is always WNAF_SIZE(w) + * - the returned skew is 0 or 1 + */ +static int secp256k1_wnaf_fixed(int *wnaf, const secp256k1_scalar *s, int w) { + int skew = 0; + int pos; + int max_pos; + int last_w; + const secp256k1_scalar *work = s; + + if (secp256k1_scalar_is_zero(s)) { + for (pos = 0; pos < WNAF_SIZE(w); pos++) { + wnaf[pos] = 0; + } + return 0; + } + + if (secp256k1_scalar_is_even(s)) { + skew = 1; + } + + wnaf[0] = secp256k1_scalar_get_bits_var(work, 0, w) + skew; + /* Compute last window size. Relevant when window size doesn't divide the + * number of bits in the scalar */ + last_w = WNAF_BITS - (WNAF_SIZE(w) - 1) * w; + + /* Store the position of the first nonzero word in max_pos to allow + * skipping leading zeros when calculating the wnaf. */ + for (pos = WNAF_SIZE(w) - 1; pos > 0; pos--) { + int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); + if(val != 0) { + break; + } + wnaf[pos] = 0; + } + max_pos = pos; + pos = 1; + + while (pos <= max_pos) { + int val = secp256k1_scalar_get_bits_var(work, pos * w, pos == WNAF_SIZE(w)-1 ? last_w : w); + if ((val & 1) == 0) { + wnaf[pos - 1] -= (1 << w); + wnaf[pos] = (val + 1); + } else { + wnaf[pos] = val; + } + /* Set a coefficient to zero if it is 1 or -1 and the proceeding digit + * is strictly negative or strictly positive respectively. Only change + * coefficients at previous positions because above code assumes that + * wnaf[pos - 1] is odd. + */ + if (pos >= 2 && ((wnaf[pos - 1] == 1 && wnaf[pos - 2] < 0) || (wnaf[pos - 1] == -1 && wnaf[pos - 2] > 0))) { + if (wnaf[pos - 1] == 1) { + wnaf[pos - 2] += 1 << w; + } else { + wnaf[pos - 2] -= 1 << w; + } + wnaf[pos - 1] = 0; + } + ++pos; + } + + return skew; +} + +struct secp256k1_pippenger_point_state { + int skew_na; + size_t input_pos; +}; + +struct secp256k1_pippenger_state { + int *wnaf_na; + struct secp256k1_pippenger_point_state* ps; +}; + +/* + * pippenger_wnaf computes the result of a multi-point multiplication as + * follows: The scalars are brought into wnaf with n_wnaf elements each. Then + * for every i < n_wnaf, first each point is added to a "bucket" corresponding + * to the point's wnaf[i]. Second, the buckets are added together such that + * r += 1*bucket[0] + 3*bucket[1] + 5*bucket[2] + ... + */ +static int secp256k1_ecmult_pippenger_wnaf(secp256k1_gej *buckets, int bucket_window, struct secp256k1_pippenger_state *state, secp256k1_gej *r, const secp256k1_scalar *sc, const secp256k1_ge *pt, size_t num) { + size_t n_wnaf = WNAF_SIZE(bucket_window+1); + size_t np; + size_t no = 0; + int i; + int j; + + for (np = 0; np < num; ++np) { + if (secp256k1_scalar_is_zero(&sc[np]) || secp256k1_ge_is_infinity(&pt[np])) { + continue; + } + state->ps[no].input_pos = np; + state->ps[no].skew_na = secp256k1_wnaf_fixed(&state->wnaf_na[no*n_wnaf], &sc[np], bucket_window+1); + no++; + } + secp256k1_gej_set_infinity(r); + + if (no == 0) { + return 1; + } + + for (i = n_wnaf - 1; i >= 0; i--) { + secp256k1_gej running_sum; + + for(j = 0; j < ECMULT_TABLE_SIZE(bucket_window+2); j++) { + secp256k1_gej_set_infinity(&buckets[j]); + } + + for (np = 0; np < no; ++np) { + int n = state->wnaf_na[np*n_wnaf + i]; + struct secp256k1_pippenger_point_state point_state = state->ps[np]; + secp256k1_ge tmp; + int idx; + + if (i == 0) { + /* correct for wnaf skew */ + int skew = point_state.skew_na; + if (skew) { + secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); + secp256k1_gej_add_ge_var(&buckets[0], &buckets[0], &tmp, NULL); + } + } + if (n > 0) { + idx = (n - 1)/2; + secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &pt[point_state.input_pos], NULL); + } else if (n < 0) { + idx = -(n + 1)/2; + secp256k1_ge_neg(&tmp, &pt[point_state.input_pos]); + secp256k1_gej_add_ge_var(&buckets[idx], &buckets[idx], &tmp, NULL); + } + } + + for(j = 0; j < bucket_window; j++) { + secp256k1_gej_double_var(r, r, NULL); + } + + secp256k1_gej_set_infinity(&running_sum); + /* Accumulate the sum: bucket[0] + 3*bucket[1] + 5*bucket[2] + 7*bucket[3] + ... + * = bucket[0] + bucket[1] + bucket[2] + bucket[3] + ... + * + 2 * (bucket[1] + 2*bucket[2] + 3*bucket[3] + ...) + * using an intermediate running sum: + * running_sum = bucket[0] + bucket[1] + bucket[2] + ... + * + * The doubling is done implicitly by deferring the final window doubling (of 'r'). + */ + for(j = ECMULT_TABLE_SIZE(bucket_window+2) - 1; j > 0; j--) { + secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[j], NULL); + secp256k1_gej_add_var(r, r, &running_sum, NULL); + } + + secp256k1_gej_add_var(&running_sum, &running_sum, &buckets[0], NULL); + secp256k1_gej_double_var(r, r, NULL); + secp256k1_gej_add_var(r, r, &running_sum, NULL); + } + return 1; +} + +/** + * Returns optimal bucket_window (number of bits of a scalar represented by a + * set of buckets) for a given number of points. + */ +static int secp256k1_pippenger_bucket_window(size_t n) { +#ifdef USE_ENDOMORPHISM + if (n <= 1) { + return 1; + } else if (n <= 4) { + return 2; + } else if (n <= 20) { + return 3; + } else if (n <= 57) { + return 4; + } else if (n <= 136) { + return 5; + } else if (n <= 235) { + return 6; + } else if (n <= 1260) { + return 7; + } else if (n <= 4420) { + return 9; + } else if (n <= 7880) { + return 10; + } else if (n <= 16050) { + return 11; + } else { + return PIPPENGER_MAX_BUCKET_WINDOW; + } +#else + if (n <= 1) { + return 1; + } else if (n <= 11) { + return 2; + } else if (n <= 45) { + return 3; + } else if (n <= 100) { + return 4; + } else if (n <= 275) { + return 5; + } else if (n <= 625) { + return 6; + } else if (n <= 1850) { + return 7; + } else if (n <= 3400) { + return 8; + } else if (n <= 9630) { + return 9; + } else if (n <= 17900) { + return 10; + } else if (n <= 32800) { + return 11; + } else { + return PIPPENGER_MAX_BUCKET_WINDOW; + } +#endif +} + +/** + * Returns the maximum optimal number of points for a bucket_window. + */ +static size_t secp256k1_pippenger_bucket_window_inv(int bucket_window) { + switch(bucket_window) { +#ifdef USE_ENDOMORPHISM + case 1: return 1; + case 2: return 4; + case 3: return 20; + case 4: return 57; + case 5: return 136; + case 6: return 235; + case 7: return 1260; + case 8: return 1260; + case 9: return 4420; + case 10: return 7880; + case 11: return 16050; + case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX; +#else + case 1: return 1; + case 2: return 11; + case 3: return 45; + case 4: return 100; + case 5: return 275; + case 6: return 625; + case 7: return 1850; + case 8: return 3400; + case 9: return 9630; + case 10: return 17900; + case 11: return 32800; + case PIPPENGER_MAX_BUCKET_WINDOW: return SIZE_MAX; +#endif + } + return 0; +} + + +#ifdef USE_ENDOMORPHISM +SECP256K1_INLINE static void secp256k1_ecmult_endo_split(secp256k1_scalar *s1, secp256k1_scalar *s2, secp256k1_ge *p1, secp256k1_ge *p2) { + secp256k1_scalar tmp = *s1; + secp256k1_scalar_split_lambda(s1, s2, &tmp); + secp256k1_ge_mul_lambda(p2, p1); + + if (secp256k1_scalar_is_high(s1)) { + secp256k1_scalar_negate(s1, s1); + secp256k1_ge_neg(p1, p1); + } + if (secp256k1_scalar_is_high(s2)) { + secp256k1_scalar_negate(s2, s2); + secp256k1_ge_neg(p2, p2); + } +} +#endif + +/** + * Returns the scratch size required for a given number of points (excluding + * base point G) without considering alignment. + */ +static size_t secp256k1_pippenger_scratch_size(size_t n_points, int bucket_window) { +#ifdef USE_ENDOMORPHISM + size_t entries = 2*n_points + 2; +#else + size_t entries = n_points + 1; +#endif + size_t entry_size = sizeof(secp256k1_ge) + sizeof(secp256k1_scalar) + sizeof(struct secp256k1_pippenger_point_state) + (WNAF_SIZE(bucket_window+1)+1)*sizeof(int); + return ((1<ps = (struct secp256k1_pippenger_point_state *) secp256k1_scratch_alloc(scratch, entries * sizeof(*state_space->ps)); + state_space->wnaf_na = (int *) secp256k1_scratch_alloc(scratch, entries*(WNAF_SIZE(bucket_window+1)) * sizeof(int)); + buckets = (secp256k1_gej *) secp256k1_scratch_alloc(scratch, (1<ps[i].skew_na = 0; + for(j = 0; j < WNAF_SIZE(bucket_window+1); j++) { + state_space->wnaf_na[i * WNAF_SIZE(bucket_window+1) + j] = 0; + } + } + for(i = 0; i < 1< max_alloc) { + break; + } + space_for_points = max_alloc - space_overhead; + + n_points = space_for_points/entry_size; + n_points = n_points > max_points ? max_points : n_points; + if (n_points > res) { + res = n_points; + } + if (n_points < max_points) { + /* A larger bucket_window may support even more points. But if we + * would choose that then the caller couldn't safely use any number + * smaller than what this function returns */ + break; + } + } + return res; +} + +/* Computes ecmult_multi by simply multiplying and adding each point. Does not + * require a scratch space */ + int secp256k1_ecmult_multi_var_simple(const secp256k1_ecmult_context *ctx, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n_points) { + size_t point_idx; + secp256k1_scalar szero; + secp256k1_gej tmpj; + + secp256k1_scalar_set_int(&szero, 0); + /* r = inp_g_sc*G */ + secp256k1_gej_set_infinity(r); + secp256k1_ecmult(ctx, r, &tmpj, &szero, inp_g_sc); + for (point_idx = 0; point_idx < n_points; point_idx++) { + secp256k1_ge point; + secp256k1_gej pointj; + secp256k1_scalar scalar; + if (!cb(&scalar, &point, point_idx, cbdata)) { + return 0; + } + /* r += scalar*point */ + secp256k1_gej_set_ge(&pointj, &point); + secp256k1_ecmult(ctx, &tmpj, &pointj, &scalar, NULL); + secp256k1_gej_add_var(r, r, &tmpj, NULL); + } + return 1; +} + +typedef int (*secp256k1_ecmult_multi_func)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t); + +int secp256k1_ecmult_multi_var(const secp256k1_ecmult_context *ctx, secp256k1_scratch *scratch, secp256k1_gej *r, const secp256k1_scalar *inp_g_sc, secp256k1_ecmult_multi_callback cb, void *cbdata, size_t n) { + size_t i; + int (*f)(const secp256k1_ecmult_context*, secp256k1_scratch*, secp256k1_gej*, const secp256k1_scalar*, secp256k1_ecmult_multi_callback cb, void*, size_t, size_t); + size_t max_points; + size_t n_batches; + size_t n_batch_points; + + secp256k1_gej_set_infinity(r); + if (inp_g_sc == NULL && n == 0) { + return 1; + } else if (n == 0) { + secp256k1_scalar szero; + secp256k1_scalar_set_int(&szero, 0); + secp256k1_ecmult(ctx, r, r, &szero, inp_g_sc); + return 1; + } + if (scratch == NULL) { + return secp256k1_ecmult_multi_var_simple(ctx, r, inp_g_sc, cb, cbdata, n); + } + + max_points = secp256k1_pippenger_max_points(scratch); + if (max_points == 0) { + return 0; + } else if (max_points > ECMULT_MAX_POINTS_PER_BATCH) { + max_points = ECMULT_MAX_POINTS_PER_BATCH; + } + n_batches = (n+max_points-1)/max_points; + n_batch_points = (n+n_batches-1)/n_batches; + + if (n_batch_points >= ECMULT_PIPPENGER_THRESHOLD) { + f = secp256k1_ecmult_pippenger_batch; + } else { + max_points = secp256k1_strauss_max_points(scratch); + if (max_points == 0) { + return 0; + } + n_batches = (n+max_points-1)/max_points; + n_batch_points = (n+n_batches-1)/n_batches; + f = secp256k1_ecmult_strauss_batch; + } + for(i = 0; i < n_batches; i++) { + size_t nbp = n < n_batch_points ? n : n_batch_points; + size_t offset = n_batch_points*i; + secp256k1_gej tmp; + if (!f(ctx, scratch, &tmp, i == 0 ? inp_g_sc : NULL, cb, cbdata, nbp, offset)) { + return 0; + } + secp256k1_gej_add_var(r, r, &tmp, NULL); + n -= nbp; + } + return 1; +} + +#endif /* SECP256K1_ECMULT_IMPL_H */ + +#endif + diff --git a/src/secp256k1/src/modules/ecdh/main_impl.h b/src/secp256k1/src/modules/ecdh/main_impl.h index bd8739eeb..74332ba1d 100644 --- a/src/secp256k1/src/modules/ecdh/main_impl.h +++ b/src/secp256k1/src/modules/ecdh/main_impl.h @@ -30,7 +30,7 @@ int secp256k1_ecdh(const secp256k1_context* ctx, unsigned char *result, const se unsigned char y[1]; secp256k1_sha256 sha; - secp256k1_ecmult_const(&res, &pt, &s); + secp256k1_ecmult_const(&res, &pt, &s,256); secp256k1_ge_set_gej(&pt, &res); /* Compute a hash of the point in compressed form * Note we cannot use secp256k1_eckey_pubkey_serialize here since it does not diff --git a/src/secp256k1/src/modules/musig/Makefile.am.include b/src/secp256k1/src/modules/musig/Makefile.am.include new file mode 100644 index 000000000..34583a23a --- /dev/null +++ b/src/secp256k1/src/modules/musig/Makefile.am.include @@ -0,0 +1,17 @@ +include_HEADERS += include/secp256k1_musig.h +noinst_HEADERS += src/modules/musig/main_impl.h +noinst_HEADERS += src/modules/musig/tests_impl.h + +noinst_PROGRAMS += example_musig +example_musig_SOURCES = src/modules/musig/example.c +example_musig_CPPFLAGS = -DSECP256K1_BUILD -I$(top_srcdir)/include $(SECP_INCLUDES) +if !ENABLE_COVERAGE +example_musig_CPPFLAGS += -DVERIFY +endif +example_musig_LDADD = libsecp256k1.la $(SECP_LIBS) +example_musig_LDFLAGS = -static + +if USE_TESTS +TESTS += example_musig +endif + diff --git a/src/secp256k1/src/modules/musig/example.c b/src/secp256k1/src/modules/musig/example.c new file mode 100644 index 000000000..70d183e7d --- /dev/null +++ b/src/secp256k1/src/modules/musig/example.c @@ -0,0 +1,166 @@ +/********************************************************************** + * Copyright (c) 2018 Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +/** + * This file demonstrates how to use the MuSig module to create a multisignature. + * Additionally, see the documentation in include/secp256k1_musig.h. + */ + +/*#include +#include +#include +#include +#include */ + + /* Number of public keys involved in creating the aggregate signature */ +#define N_SIGNERS 3 + /* Create a key pair and store it in seckey and pubkey */ +int create_key(const secp256k1_context* ctx, unsigned char* seckey, secp256k1_pubkey* pubkey) { + int ret; + FILE *frand = fopen("/dev/urandom", "r"); + if (frand == NULL) { + return 0; + } + do { + if(!fread(seckey, 32, 1, frand)) { + fclose(frand); + return 0; + } + /* The probability that this not a valid secret key is approximately 2^-128 */ + } while (!secp256k1_ec_seckey_verify(ctx, seckey)); + fclose(frand); + ret = secp256k1_ec_pubkey_create(ctx, pubkey, seckey); + return ret; +} + +/* Sign a message hash with the given key pairs and store the result in sig */ +int sign(const secp256k1_context* ctx, unsigned char seckeys[][32], const secp256k1_pubkey* pubkeys, const unsigned char* msg32, secp256k1_schnorrsig *sig) { + secp256k1_musig_session musig_session[N_SIGNERS]; + unsigned char nonce_commitment[N_SIGNERS][32]; + const unsigned char *nonce_commitment_ptr[N_SIGNERS]; + secp256k1_musig_session_signer_data signer_data[N_SIGNERS][N_SIGNERS]; + secp256k1_pubkey nonce[N_SIGNERS]; + int i, j; + secp256k1_musig_partial_signature partial_sig[N_SIGNERS]; + + for (i = 0; i < N_SIGNERS; i++) { + FILE *frand; + unsigned char session_id32[32]; + unsigned char pk_hash[32]; + secp256k1_pubkey combined_pk; + + /* Create combined pubkey and initialize signer data */ + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, pk_hash, pubkeys, N_SIGNERS)) { + return 0; + } + /* Create random session ID. It is absolutely necessary that the session ID + * is unique for every call of secp256k1_musig_session_initialize. Otherwise + * it's trivial for an attacker to extract the secret key! */ + frand = fopen("/dev/urandom", "r"); + if(frand == NULL) { + return 0; + } + if (!fread(session_id32, 32, 1, frand)) { + fclose(frand); + return 0; + } + fclose(frand); + /* Initialize session */ + if (!secp256k1_musig_session_initialize(ctx, &musig_session[i], signer_data[i], nonce_commitment[i], session_id32, msg32, &combined_pk, pk_hash, N_SIGNERS, i, seckeys[i])) { + return 0; + } + nonce_commitment_ptr[i] = &nonce_commitment[i][0]; + } + /* Communication round 1: Exchange nonce commitments */ + for (i = 0; i < N_SIGNERS; i++) { + /* Set nonce commitments in the signer data and get the own public nonce */ + if (!secp256k1_musig_session_get_public_nonce(ctx, &musig_session[i], signer_data[i], &nonce[i], nonce_commitment_ptr, N_SIGNERS)) { + return 0; + } + } + /* Communication round 2: Exchange nonces */ + for (i = 0; i < N_SIGNERS; i++) { + for (j = 0; j < N_SIGNERS; j++) { + if (!secp256k1_musig_set_nonce(ctx, &signer_data[i][j], &nonce[j])) { + /* Signer j's nonce does not match the nonce commitment. In this case + * abort the protocol. If you make another attempt at finishing the + * protocol, create a new session (with a fresh session ID!). */ + return 0; + } + } + if (!secp256k1_musig_session_combine_nonces(ctx, &musig_session[i], signer_data[i], N_SIGNERS, NULL, NULL)) { + return 0; + } + } + for (i = 0; i < N_SIGNERS; i++) { + if (!secp256k1_musig_partial_sign(ctx, &musig_session[i], &partial_sig[i])) { + return 0; + } + } + /* Communication round 3: Exchange partial signatures */ + for (i = 0; i < N_SIGNERS; i++) { + for (j = 0; j < N_SIGNERS; j++) { + /* To check whether signing was successful, it suffices to either verify + * the the combined signature with the combined public key using + * secp256k1_schnorrsig_verify, or verify all partial signatures of all + * signers individually. Verifying the combined signature is cheaper but + * verifying the individual partial signatures has the advantage that it + * can be used to determine which of the partial signatures are invalid + * (if any), i.e., which of the partial signatures cause the combined + * signature to be invalid and thus the protocol run to fail. It's also + * fine to first verify the combined sig, and only verify the individual + * sigs if it does not work. + */ + if (!secp256k1_musig_partial_sig_verify(ctx, &musig_session[i], &signer_data[i][j], &partial_sig[j], &pubkeys[j])) { + return 0; + } + } + } + return secp256k1_musig_partial_sig_combine(ctx, &musig_session[0], sig, partial_sig, N_SIGNERS); +} + + int testmain(void) { + secp256k1_context* ctx; + int i; + unsigned char seckeys[N_SIGNERS][32]; + secp256k1_pubkey pubkeys[N_SIGNERS]; + secp256k1_pubkey combined_pk; + unsigned char msg[32] = "this_could_be_the_hash_of_a_msg"; + secp256k1_schnorrsig sig; + + /* Create a context for signing and verification */ + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + fprintf(stderr,"Creating key pairs......"); + for (i = 0; i < N_SIGNERS; i++) { + if (!create_key(ctx, seckeys[i], &pubkeys[i])) { + fprintf(stderr,"FAILED\n"); + return 1; + } + } + fprintf(stderr,"ok\n"); + fprintf(stderr,"Combining public keys..."); + if (!secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk, NULL, pubkeys, N_SIGNERS)) { + fprintf(stderr,"FAILED\n"); + return 1; + } + fprintf(stderr,"ok\n"); + fprintf(stderr,"Signing message........."); + if (!sign(ctx, seckeys, pubkeys, msg, &sig)) { + fprintf(stderr,"FAILED\n"); + return 1; + } + fprintf(stderr,"ok\n"); + fprintf(stderr,"Verifying signature....."); + if (!secp256k1_schnorrsig_verify(ctx, &sig, msg, &combined_pk)) { + fprintf(stderr,"FAILED\n"); + return 1; + } + fprintf(stderr,"ok\n"); + secp256k1_context_destroy(ctx); + return 0; +} + + diff --git a/src/secp256k1/src/modules/musig/main_impl.h b/src/secp256k1/src/modules/musig/main_impl.h new file mode 100644 index 000000000..8bd2f1831 --- /dev/null +++ b/src/secp256k1/src/modules/musig/main_impl.h @@ -0,0 +1,631 @@ + +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra, Jonas Nick * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_MUSIG_MAIN_ +#define _SECP256K1_MODULE_MUSIG_MAIN_ + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_musig.h" +#include "hash.h" + +/* Computes ell = SHA256(pk[0], ..., pk[np-1]) */ +static int secp256k1_musig_compute_ell(const secp256k1_context *ctx, unsigned char *ell, const secp256k1_pubkey *pk, size_t np) { + secp256k1_sha256 sha; + size_t i; + + secp256k1_sha256_initialize(&sha); + for (i = 0; i < np; i++) { + unsigned char ser[33]; + size_t serlen = sizeof(ser); + if (!secp256k1_ec_pubkey_serialize(ctx, ser, &serlen, &pk[i], SECP256K1_EC_COMPRESSED)) { + return 0; + } + secp256k1_sha256_write(&sha, ser, serlen); + } + secp256k1_sha256_finalize(&sha, ell); + return 1; +} + +/* Initializes SHA256 with fixed midstate. This midstate was computed by applying + * SHA256 to SHA256("MuSig coefficient")||SHA256("MuSig coefficient"). */ +static void secp256k1_musig_sha256_init_tagged(secp256k1_sha256 *sha) { + secp256k1_sha256_initialize(sha); + + sha->s[0] = 0x0fd0690cul; + sha->s[1] = 0xfefeae97ul; + sha->s[2] = 0x996eac7ful; + sha->s[3] = 0x5c30d864ul; + sha->s[4] = 0x8c4a0573ul; + sha->s[5] = 0xaca1a22ful; + sha->s[6] = 0x6f43b801ul; + sha->s[7] = 0x85ce27cdul; + sha->bytes = 64; +} + +/* Compute r = SHA256(ell, idx). The four bytes of idx are serialized least significant byte first. */ +static void secp256k1_musig_coefficient(secp256k1_scalar *r, const unsigned char *ell, uint32_t idx) { + secp256k1_sha256 sha; + unsigned char buf[32]; + size_t i; + + secp256k1_musig_sha256_init_tagged(&sha); + secp256k1_sha256_write(&sha, ell, 32); + /* We're hashing the index of the signer instead of its public key as specified + * in the MuSig paper. This reduces the total amount of data that needs to be + * hashed. + * Additionally, it prevents creating identical musig_coefficients for identical + * public keys. A participant Bob could choose his public key to be the same as + * Alice's, then replay Alice's messages (nonce and partial signature) to create + * a valid partial signature. This is not a problem for MuSig per se, but could + * result in subtle issues with protocols building on threshold signatures. + * With the assumption that public keys are unique, hashing the index is + * equivalent to hashing the public key. Because the public key can be + * identified by the index given the ordered list of public keys (included in + * ell), the index is just a different encoding of the public key.*/ + for (i = 0; i < sizeof(uint32_t); i++) { + unsigned char c = idx; + secp256k1_sha256_write(&sha, &c, 1); + idx >>= 8; + } + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(r, buf, NULL); +} + +typedef struct { + const secp256k1_context *ctx; + unsigned char ell[32]; + const secp256k1_pubkey *pks; +} secp256k1_musig_pubkey_combine_ecmult_data; + +/* Callback for batch EC multiplication to compute ell_0*P0 + ell_1*P1 + ... */ +static int secp256k1_musig_pubkey_combine_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_musig_pubkey_combine_ecmult_data *ctx = (secp256k1_musig_pubkey_combine_ecmult_data *) data; + secp256k1_musig_coefficient(sc, ctx->ell, idx); + return secp256k1_pubkey_load(ctx->ctx, pt, &ctx->pks[idx]); +} + + +static void secp256k1_musig_signers_init(secp256k1_musig_session_signer_data *signers, uint32_t n_signers) { + uint32_t i; + for (i = 0; i < n_signers; i++) { + memset(&signers[i], 0, sizeof(signers[i])); + signers[i].index = i; + signers[i].present = 0; + } +} + +int secp256k1_musig_pubkey_combine(const secp256k1_context* ctx, secp256k1_scratch_space *scratch, secp256k1_pubkey *combined_pk, unsigned char *pk_hash32, const secp256k1_pubkey *pubkeys, size_t n_pubkeys) { + secp256k1_musig_pubkey_combine_ecmult_data ecmult_data; + secp256k1_gej pkj; + secp256k1_ge pkp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(pubkeys != NULL); + ARG_CHECK(n_pubkeys > 0); + + ecmult_data.ctx = ctx; + ecmult_data.pks = pubkeys; + if (!secp256k1_musig_compute_ell(ctx, ecmult_data.ell, pubkeys, n_pubkeys)) { + return 0; + } + if (!secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &pkj, NULL, secp256k1_musig_pubkey_combine_callback, (void *) &ecmult_data, n_pubkeys)) { + return 0; + } + secp256k1_ge_set_gej(&pkp, &pkj); + secp256k1_pubkey_save(combined_pk, &pkp); + + if (pk_hash32 != NULL) { + memcpy(pk_hash32, ecmult_data.ell, 32); + } + return 1; +} + +int secp256k1_musig_session_initialize(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, unsigned char *nonce_commitment32, const unsigned char *session_id32, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, size_t n_signers, size_t my_index, const unsigned char *seckey) { + unsigned char combined_ser[33]; + size_t combined_ser_size = sizeof(combined_ser); + int overflow; + secp256k1_scalar secret; + secp256k1_scalar mu; + secp256k1_sha256 sha; + secp256k1_gej rj; + secp256k1_ge rp; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(nonce_commitment32 != NULL); + ARG_CHECK(session_id32 != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(seckey != NULL); + + memset(session, 0, sizeof(*session)); + + if (msg32 != NULL) { + memcpy(session->msg, msg32, 32); + session->msg_is_set = 1; + } else { + session->msg_is_set = 0; + } + memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + memcpy(session->pk_hash, pk_hash32, 32); + session->nonce_is_set = 0; + session->has_secret_data = 1; + if (n_signers == 0 || my_index >= n_signers) { + return 0; + } + if (n_signers > UINT32_MAX) { + return 0; + } + session->n_signers = (uint32_t) n_signers; + secp256k1_musig_signers_init(signers, session->n_signers); + session->nonce_commitments_hash_is_set = 0; + + /* Compute secret key */ + secp256k1_scalar_set_b32(&secret, seckey, &overflow); + if (overflow) { + secp256k1_scalar_clear(&secret); + return 0; + } + secp256k1_musig_coefficient(&mu, pk_hash32, (uint32_t) my_index); + secp256k1_scalar_mul(&secret, &secret, &mu); + secp256k1_scalar_get_b32(session->seckey, &secret); + + /* Compute secret nonce */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, session_id32, 32); + if (session->msg_is_set) { + secp256k1_sha256_write(&sha, msg32, 32); + } + secp256k1_ec_pubkey_serialize(ctx, combined_ser, &combined_ser_size, combined_pk, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, combined_ser, combined_ser_size); + secp256k1_sha256_write(&sha, seckey, 32); + secp256k1_sha256_finalize(&sha, session->secnonce); + secp256k1_scalar_set_b32(&secret, session->secnonce, &overflow); + if (overflow) { + secp256k1_scalar_clear(&secret); + return 0; + } + + /* Compute public nonce and commitment */ + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &secret); + secp256k1_ge_set_gej(&rp, &rj); + secp256k1_pubkey_save(&session->nonce, &rp); + + if (nonce_commitment32 != NULL) { + unsigned char commit[33]; + size_t commit_size = sizeof(commit); + secp256k1_sha256_initialize(&sha); + secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, &session->nonce, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, commit, commit_size); + secp256k1_sha256_finalize(&sha, nonce_commitment32); + } + + secp256k1_scalar_clear(&secret); + return 1; +} + +int secp256k1_musig_session_get_public_nonce(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, secp256k1_pubkey *nonce, const unsigned char *const *commitments, size_t n_commitments) { + secp256k1_sha256 sha; + unsigned char nonce_commitments_hash[32]; + size_t i; + (void) ctx; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(nonce != NULL); + ARG_CHECK(commitments != NULL); + + if (!session->has_secret_data || n_commitments != session->n_signers) { + return 0; + } + for (i = 0; i < n_commitments; i++) { + ARG_CHECK(commitments[i] != NULL); + } + + secp256k1_sha256_initialize(&sha); + for (i = 0; i < n_commitments; i++) { + memcpy(signers[i].nonce_commitment, commitments[i], 32); + secp256k1_sha256_write(&sha, commitments[i], 32); + } + secp256k1_sha256_finalize(&sha, nonce_commitments_hash); + if (session->nonce_commitments_hash_is_set + && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { + /* Abort if get_public_nonce has been called before with a different array of + * commitments. */ + return 0; + } + memcpy(session->nonce_commitments_hash, nonce_commitments_hash, 32); + session->nonce_commitments_hash_is_set = 1; + memcpy(nonce, &session->nonce, sizeof(*nonce)); + return 1; +} + +int secp256k1_musig_session_initialize_verifier(const secp256k1_context* ctx, secp256k1_musig_session *session, secp256k1_musig_session_signer_data *signers, const unsigned char *msg32, const secp256k1_pubkey *combined_pk, const unsigned char *pk_hash32, const unsigned char *const *commitments, size_t n_signers) { + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + ARG_CHECK(combined_pk != NULL); + ARG_CHECK(pk_hash32 != NULL); + ARG_CHECK(commitments != NULL); + /* Check n_signers before checking commitments to allow testing the case where + * n_signers is big without allocating the space. */ + if (n_signers > UINT32_MAX) { + return 0; + } + for (i = 0; i < n_signers; i++) { + ARG_CHECK(commitments[i] != NULL); + } + (void) ctx; + + memset(session, 0, sizeof(*session)); + + memcpy(&session->combined_pk, combined_pk, sizeof(*combined_pk)); + if (n_signers == 0) { + return 0; + } + session->n_signers = (uint32_t) n_signers; + secp256k1_musig_signers_init(signers, session->n_signers); + + memcpy(session->pk_hash, pk_hash32, 32); + session->nonce_is_set = 0; + session->msg_is_set = 0; + if (msg32 != NULL) { + memcpy(session->msg, msg32, 32); + session->msg_is_set = 1; + } + session->has_secret_data = 0; + session->nonce_commitments_hash_is_set = 0; + + for (i = 0; i < n_signers; i++) { + memcpy(signers[i].nonce_commitment, commitments[i], 32); + } + return 1; +} + +int secp256k1_musig_set_nonce(const secp256k1_context* ctx, secp256k1_musig_session_signer_data *signer, const secp256k1_pubkey *nonce) { + unsigned char commit[33]; + size_t commit_size = sizeof(commit); + secp256k1_sha256 sha; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(signer != NULL); + ARG_CHECK(nonce != NULL); + + secp256k1_sha256_initialize(&sha); + secp256k1_ec_pubkey_serialize(ctx, commit, &commit_size, nonce, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, commit, commit_size); + secp256k1_sha256_finalize(&sha, commit); + + if (memcmp(commit, signer->nonce_commitment, 32) != 0) { + return 0; + } + memcpy(&signer->nonce, nonce, sizeof(*nonce)); + signer->present = 1; + return 1; +} + +int secp256k1_musig_session_combine_nonces(const secp256k1_context* ctx, secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signers, size_t n_signers, int *nonce_is_negated, const secp256k1_pubkey *adaptor) { + secp256k1_gej combined_noncej; + secp256k1_ge combined_noncep; + secp256k1_ge noncep; + secp256k1_sha256 sha; + unsigned char nonce_commitments_hash[32]; + size_t i; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(signers != NULL); + + if (n_signers != session->n_signers) { + return 0; + } + secp256k1_sha256_initialize(&sha); + secp256k1_gej_set_infinity(&combined_noncej); + for (i = 0; i < n_signers; i++) { + if (!signers[i].present) { + return 0; + } + secp256k1_sha256_write(&sha, signers[i].nonce_commitment, 32); + secp256k1_pubkey_load(ctx, &noncep, &signers[i].nonce); + secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); + } + secp256k1_sha256_finalize(&sha, nonce_commitments_hash); + /* Either the session is a verifier session or or the nonce_commitments_hash has + * been set in `musig_session_get_public_nonce`. */ + VERIFY_CHECK(!session->has_secret_data || session->nonce_commitments_hash_is_set); + if (session->has_secret_data + && memcmp(session->nonce_commitments_hash, nonce_commitments_hash, 32) != 0) { + /* If the signers' commitments changed between get_public_nonce and now we + * have to abort because in that case they may have seen our nonce before + * creating their commitment. That can happen if the signer_data given to + * this function is different to the signer_data given to get_public_nonce. + * */ + return 0; + } + + /* Add public adaptor to nonce */ + if (adaptor != NULL) { + secp256k1_pubkey_load(ctx, &noncep, adaptor); + secp256k1_gej_add_ge_var(&combined_noncej, &combined_noncej, &noncep, NULL); + } + secp256k1_ge_set_gej(&combined_noncep, &combined_noncej); + if (secp256k1_fe_is_quad_var(&combined_noncep.y)) { + session->nonce_is_negated = 0; + } else { + session->nonce_is_negated = 1; + secp256k1_ge_neg(&combined_noncep, &combined_noncep); + } + if (nonce_is_negated != NULL) { + *nonce_is_negated = session->nonce_is_negated; + } + secp256k1_pubkey_save(&session->combined_nonce, &combined_noncep); + session->nonce_is_set = 1; + return 1; +} + +int secp256k1_musig_session_set_msg(const secp256k1_context* ctx, secp256k1_musig_session *session, const unsigned char *msg32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(session != NULL); + ARG_CHECK(msg32 != NULL); + + if (session->msg_is_set) { + return 0; + } + memcpy(session->msg, msg32, 32); + session->msg_is_set = 1; + return 1; +} + +int secp256k1_musig_partial_signature_serialize(const secp256k1_context* ctx, unsigned char *out32, const secp256k1_musig_partial_signature* sig) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out32 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out32, sig->data, 32); + return 1; +} + +int secp256k1_musig_partial_signature_parse(const secp256k1_context* ctx, secp256k1_musig_partial_signature* sig, const unsigned char *in32) { + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in32 != NULL); + memcpy(sig->data, in32, 32); + return 1; +} + +/* Compute msghash = SHA256(combined_nonce, combined_pk, msg) */ +static int secp256k1_musig_compute_messagehash(const secp256k1_context *ctx, unsigned char *msghash, const secp256k1_musig_session *session) { + unsigned char buf[33]; + size_t bufsize = 33; + secp256k1_ge rp; + secp256k1_sha256 sha; + + secp256k1_sha256_initialize(&sha); + if (!session->nonce_is_set) { + return 0; + } + secp256k1_pubkey_load(ctx, &rp, &session->combined_nonce); + secp256k1_fe_get_b32(buf, &rp.x); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &bufsize, &session->combined_pk, SECP256K1_EC_COMPRESSED); + VERIFY_CHECK(bufsize == 33); + secp256k1_sha256_write(&sha, buf, bufsize); + if (!session->msg_is_set) { + return 0; + } + secp256k1_sha256_write(&sha, session->msg, 32); + secp256k1_sha256_finalize(&sha, msghash); + return 1; +} + +int secp256k1_musig_partial_sign(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_musig_partial_signature *partial_sig) { + unsigned char msghash[32]; + int overflow; + secp256k1_scalar sk; + secp256k1_scalar e, k; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(session != NULL); + + if (!session->nonce_is_set || !session->has_secret_data) { + return 0; + } + + /* build message hash */ + if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { + return 0; + } + secp256k1_scalar_set_b32(&e, msghash, NULL); + + secp256k1_scalar_set_b32(&sk, session->seckey, &overflow); + if (overflow) { + secp256k1_scalar_clear(&sk); + return 0; + } + + secp256k1_scalar_set_b32(&k, session->secnonce, &overflow); + if (overflow || secp256k1_scalar_is_zero(&k)) { + secp256k1_scalar_clear(&sk); + secp256k1_scalar_clear(&k); + return 0; + } + if (session->nonce_is_negated) { + secp256k1_scalar_negate(&k, &k); + } + + /* Sign */ + secp256k1_scalar_mul(&e, &e, &sk); + secp256k1_scalar_add(&e, &e, &k); + secp256k1_scalar_get_b32(&partial_sig->data[0], &e); + secp256k1_scalar_clear(&sk); + secp256k1_scalar_clear(&k); + + return 1; +} + +int secp256k1_musig_partial_sig_combine(const secp256k1_context* ctx, const secp256k1_musig_session *session, secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_sigs) { + size_t i; + secp256k1_scalar s; + secp256k1_ge noncep; + (void) ctx; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(partial_sigs != NULL); + ARG_CHECK(session != NULL); + + if (!session->nonce_is_set) { + return 0; + } + if (n_sigs != session->n_signers) { + return 0; + } + secp256k1_scalar_clear(&s); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + + secp256k1_scalar_set_b32(&term, partial_sigs[i].data, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_add(&s, &s, &term); + } + + secp256k1_pubkey_load(ctx, &noncep, &session->combined_nonce); + VERIFY_CHECK(secp256k1_fe_is_quad_var(&noncep.y)); + secp256k1_fe_normalize(&noncep.x); + secp256k1_fe_get_b32(&sig->data[0], &noncep.x); + secp256k1_scalar_get_b32(&sig->data[32], &s); + + return 1; +} + +int secp256k1_musig_partial_sig_verify(const secp256k1_context* ctx, const secp256k1_musig_session *session, const secp256k1_musig_session_signer_data *signer, const secp256k1_musig_partial_signature *partial_sig, const secp256k1_pubkey *pubkey) { + unsigned char msghash[32]; + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_scalar mu; + secp256k1_gej rj; + secp256k1_ge rp; + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(session != NULL); + ARG_CHECK(signer != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(pubkey != NULL); + + if (!session->nonce_is_set || !signer->present) { + return 0; + } + secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); + if (overflow) { + return 0; + } + if (!secp256k1_musig_compute_messagehash(ctx, msghash, session)) { + return 0; + } + secp256k1_scalar_set_b32(&e, msghash, NULL); + + /* Multiplying the messagehash by the musig coefficient is equivalent + * to multiplying the signer's public key by the coefficient, except + * much easier to do. */ + secp256k1_musig_coefficient(&mu, session->pk_hash, signer->index); + secp256k1_scalar_mul(&e, &e, &mu); + + if (!secp256k1_pubkey_load(ctx, &rp, &signer->nonce)) { + return 0; + } + + if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pubkey)) { + return 0; + } + if (!session->nonce_is_negated) { + secp256k1_ge_neg(&rp, &rp); + } + secp256k1_gej_add_ge_var(&rj, &rj, &rp, NULL); + + return secp256k1_gej_is_infinity(&rj); +} + +int secp256k1_musig_partial_sig_adapt(const secp256k1_context* ctx, secp256k1_musig_partial_signature *adaptor_sig, const secp256k1_musig_partial_signature *partial_sig, const unsigned char *sec_adaptor32, int nonce_is_negated) { + secp256k1_scalar s; + secp256k1_scalar t; + int overflow; + + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(adaptor_sig != NULL); + ARG_CHECK(partial_sig != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + + secp256k1_scalar_set_b32(&s, partial_sig->data, &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_set_b32(&t, sec_adaptor32, &overflow); + if (overflow) { + secp256k1_scalar_clear(&t); + return 0; + } + + if (nonce_is_negated) { + secp256k1_scalar_negate(&t, &t); + } + + secp256k1_scalar_add(&s, &s, &t); + secp256k1_scalar_get_b32(adaptor_sig->data, &s); + secp256k1_scalar_clear(&t); + return 1; +} + +int secp256k1_musig_extract_secret_adaptor(const secp256k1_context* ctx, unsigned char *sec_adaptor32, const secp256k1_schnorrsig *sig, const secp256k1_musig_partial_signature *partial_sigs, size_t n_partial_sigs, int nonce_is_negated) { + secp256k1_scalar t; + secp256k1_scalar s; + int overflow; + size_t i; + + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sec_adaptor32 != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(partial_sigs != NULL); + + secp256k1_scalar_set_b32(&t, &sig->data[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_negate(&t, &t); + + for (i = 0; i < n_partial_sigs; i++) { + secp256k1_scalar_set_b32(&s, partial_sigs[i].data, &overflow); + if (overflow) { + secp256k1_scalar_clear(&t); + return 0; + } + secp256k1_scalar_add(&t, &t, &s); + } + + if (!nonce_is_negated) { + secp256k1_scalar_negate(&t, &t); + } + secp256k1_scalar_get_b32(sec_adaptor32, &t); + secp256k1_scalar_clear(&t); + return 1; +} + +#endif + diff --git a/src/secp256k1/src/modules/musig/tests_impl.h b/src/secp256k1/src/modules/musig/tests_impl.h new file mode 100644 index 000000000..c58ae95af --- /dev/null +++ b/src/secp256k1/src/modules/musig/tests_impl.h @@ -0,0 +1,758 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_MUSIG_TESTS_ +#define _SECP256K1_MODULE_MUSIG_TESTS_ + +#include "secp256k1_musig.h" + +void musig_api_tests(secp256k1_scratch_space *scratch) { + secp256k1_scratch_space *scratch_small; + secp256k1_musig_session session[2]; + secp256k1_musig_session verifier_session; + secp256k1_musig_session_signer_data signer0[2]; + secp256k1_musig_session_signer_data signer1[2]; + secp256k1_musig_session_signer_data verifier_signer_data[2]; + secp256k1_musig_partial_signature partial_sig[2]; + secp256k1_musig_partial_signature partial_sig_adapted[2]; + secp256k1_musig_partial_signature partial_sig_overflow; + secp256k1_schnorrsig final_sig; + secp256k1_schnorrsig final_sig_cmp; + + unsigned char buf[32]; + unsigned char sk[2][32]; + unsigned char ones[32]; + unsigned char session_id[2][32]; + unsigned char nonce_commitment[2][32]; + int nonce_is_negated; + const unsigned char *ncs[2]; + unsigned char msg[32]; + unsigned char msghash[32]; + secp256k1_pubkey combined_pk; + unsigned char pk_hash[32]; + secp256k1_pubkey pk[2]; + + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor1[32]; + secp256k1_pubkey adaptor; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + + memset(ones, 0xff, 32); + + secp256k1_rand256(session_id[0]); + secp256k1_rand256(session_id[1]); + secp256k1_rand256(sk[0]); + secp256k1_rand256(sk[1]); + secp256k1_rand256(msg); + secp256k1_rand256(sec_adaptor); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &adaptor, sec_adaptor) == 1); + + /** main test body **/ + + /* Key combination */ + ecount = 0; + CHECK(secp256k1_musig_pubkey_combine(none, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_pubkey_combine(sign, scratch, &combined_pk, pk_hash, pk, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(ecount == 2); + /* pubkey_combine does not require a scratch space */ + CHECK(secp256k1_musig_pubkey_combine(vrfy, NULL, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(ecount == 2); + /* If a scratch space is given it shouldn't be too small */ + scratch_small = secp256k1_scratch_space_create(ctx, 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch_small, &combined_pk, pk_hash, pk, 2) == 0); + secp256k1_scratch_space_destroy(scratch_small); + CHECK(ecount == 2); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, NULL, pk_hash, pk, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, NULL, pk, 2) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 0) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, NULL, 0) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_pubkey_combine(vrfy, scratch, &combined_pk, pk_hash, pk, 2) == 1); + + /** Session creation **/ + ecount = 0; + CHECK(secp256k1_musig_session_initialize(none, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_initialize(vrfy, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_initialize(sign, NULL, signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], NULL, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, NULL, session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], NULL, msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(ecount == 6); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, NULL, pk_hash, 2, 0, sk[0]) == 0); + CHECK(ecount == 7); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, NULL, 2, 0, sk[0]) == 0); + CHECK(ecount == 8); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 0, 0, sk[0]) == 0); + CHECK(ecount == 8); + /* If more than UINT32_MAX fits in a size_t, test that session_initialize + * rejects n_signers that high. */ + if (SIZE_MAX > UINT32_MAX) { + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, ((size_t) UINT32_MAX) + 2, 0, sk[0]) == 0); + } + CHECK(ecount == 8); + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, NULL) == 0); + CHECK(ecount == 9); + /* secret key overflows */ + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, ones) == 0); + CHECK(ecount == 9); + + + { + secp256k1_musig_session session_without_msg; + CHECK(secp256k1_musig_session_initialize(sign, &session_without_msg, signer0, nonce_commitment[0], session_id[0], NULL, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_set_msg(none, &session_without_msg, msg) == 1); + CHECK(secp256k1_musig_session_set_msg(none, &session_without_msg, msg) == 0); + } + CHECK(secp256k1_musig_session_initialize(sign, &session[0], signer0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(sign, &session[1], signer1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + + ecount = 0; + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_session_initialize_verifier(none, NULL, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, NULL, &combined_pk, pk_hash, ncs, 2) == 1); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, NULL, pk_hash, ncs, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, NULL, ncs, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, NULL, 2) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 0) == 0); + CHECK(ecount == 4); + if (SIZE_MAX > UINT32_MAX) { + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, ((size_t) UINT32_MAX) + 2) == 0); + } + CHECK(ecount == 4); + CHECK(secp256k1_musig_session_initialize_verifier(none, &verifier_session, verifier_signer_data, msg, &combined_pk, pk_hash, ncs, 2) == 1); + + CHECK(secp256k1_musig_compute_messagehash(none, msghash, &verifier_session) == 0); + CHECK(secp256k1_musig_compute_messagehash(none, msghash, &session[0]) == 0); + + /** Signing step 0 -- exchange nonce commitments */ + ecount = 0; + { + secp256k1_pubkey nonce; + + /* Can obtain public nonce after commitments have been exchanged; still can't sign */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 0); + CHECK(ecount == 0); + } + + /** Signing step 1 -- exchange nonces */ + ecount = 0; + { + secp256k1_pubkey public_nonce[3]; + + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_session_get_public_nonce(none, NULL, signer0, &public_nonce[0], ncs, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], NULL, &public_nonce[0], ncs, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, NULL, ncs, 2) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], NULL, 2) == 0); + CHECK(ecount == 4); + /* Number of commitments and number of signers are different */ + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 1) == 0); + CHECK(ecount == 4); + + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[0], signer0, &public_nonce[0], ncs, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(none, &session[1], signer1, &public_nonce[1], ncs, 2) == 1); + + CHECK(secp256k1_musig_set_nonce(none, &signer0[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[0]) == 0); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer0[1], &public_nonce[1]) == 1); + CHECK(ecount == 4); + + CHECK(secp256k1_musig_set_nonce(none, NULL, &public_nonce[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], NULL) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_set_nonce(none, &signer1[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &signer1[1], &public_nonce[1]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[0], &public_nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(none, &verifier_signer_data[1], &public_nonce[1]) == 1); + + ecount = 0; + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, NULL, signer0, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], NULL, 2, &nonce_is_negated, &adaptor) == 0); + CHECK(ecount == 2); + /* Number of signers differs from number during intialization */ + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 1, &nonce_is_negated, &adaptor) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, NULL, &adaptor) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, NULL) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(none, &session[0], signer0, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &session[1], signer0, 2, &nonce_is_negated, &adaptor) == 1); + CHECK(secp256k1_musig_session_combine_nonces(none, &verifier_session, verifier_signer_data, 2, &nonce_is_negated, &adaptor) == 1); + } + + /** Signing step 2 -- partial signatures */ + ecount = 0; + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_musig_partial_sign(none, NULL, &partial_sig[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sign(none, &session[0], NULL) == 0); + CHECK(ecount == 2); + + CHECK(secp256k1_musig_partial_sign(none, &session[0], &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_sign(none, &session[1], &partial_sig[1]) == 1); + /* observer can't sign */ + CHECK(secp256k1_musig_partial_sign(none, &verifier_session, &partial_sig[2]) == 0); + CHECK(ecount == 2); + + ecount = 0; + CHECK(secp256k1_musig_partial_signature_serialize(none, buf, &partial_sig[0]) == 1); + CHECK(secp256k1_musig_partial_signature_serialize(none, NULL, &partial_sig[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_signature_serialize(none, buf, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], buf) == 1); + CHECK(secp256k1_musig_partial_signature_parse(none, NULL, buf) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig[0], NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_signature_parse(none, &partial_sig_overflow, ones) == 1); + + /** Partial signature verification */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_verify(none, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_verify(sign, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[1], &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, NULL, &signer0[0], &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], NULL, &partial_sig[0], &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], NULL, &pk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig_overflow, &pk[0]) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], NULL) == 0); + CHECK(ecount == 6); + + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[0], &signer0[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &session[1], &signer1[1], &partial_sig[1], &pk[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[0], &partial_sig[0], &pk[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(vrfy, &verifier_session, &verifier_signer_data[1], &partial_sig[1], &pk[1]) == 1); + CHECK(ecount == 6); + + /** Adaptor signature verification */ + memcpy(&partial_sig_adapted[1], &partial_sig[1], sizeof(partial_sig_adapted[1])); + ecount = 0; + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], sec_adaptor, nonce_is_negated) == 1); + CHECK(secp256k1_musig_partial_sig_adapt(none, NULL, &partial_sig[0], sec_adaptor, 0) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], NULL, sec_adaptor, 0) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig_overflow, sec_adaptor, nonce_is_negated) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], NULL, 0) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_musig_partial_sig_adapt(none, &partial_sig_adapted[0], &partial_sig[0], ones, nonce_is_negated) == 0); + CHECK(ecount == 3); + + /** Signing combining and verification */ + ecount = 0; + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 2) == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig_cmp, partial_sig_adapted, 2) == 1); + CHECK(memcmp(&final_sig, &final_sig_cmp, sizeof(final_sig)) == 0); + + CHECK(secp256k1_musig_partial_sig_combine(none, NULL, &final_sig, partial_sig_adapted, 2) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], NULL, partial_sig_adapted, 2) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, NULL, 2) == 0); + CHECK(ecount == 3); + { + secp256k1_musig_partial_signature partial_sig_tmp[2]; + partial_sig_tmp[0] = partial_sig_adapted[0]; + partial_sig_tmp[1] = partial_sig_overflow; + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_tmp, 2) == 0); + } + CHECK(ecount == 3); + /* Wrong number of partial sigs */ + CHECK(secp256k1_musig_partial_sig_combine(none, &session[0], &final_sig, partial_sig_adapted, 1) == 0); + CHECK(ecount == 3); + + CHECK(secp256k1_schnorrsig_verify(vrfy, &final_sig, msg, &combined_pk) == 1); + + /** Secret adaptor can be extracted from signature */ + ecount = 0; + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, nonce_is_negated) == 1); + CHECK(memcmp(sec_adaptor, sec_adaptor1, 32) == 0); + CHECK(secp256k1_musig_extract_secret_adaptor(none, NULL, &final_sig, partial_sig, 2, 0) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, NULL, partial_sig, 2, 0) == 0); + CHECK(ecount == 2); + { + secp256k1_schnorrsig final_sig_tmp = final_sig; + memcpy(&final_sig_tmp.data[32], ones, 32); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig_tmp, partial_sig, 2, nonce_is_negated) == 0); + } + CHECK(ecount == 2); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, NULL, 2, 0) == 0); + CHECK(ecount == 3); + { + secp256k1_musig_partial_signature partial_sig_tmp[2]; + partial_sig_tmp[0] = partial_sig[0]; + partial_sig_tmp[1] = partial_sig_overflow; + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig_tmp, 2, nonce_is_negated) == 0); + } + CHECK(ecount == 3); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 0, 0) == 1); + CHECK(secp256k1_musig_extract_secret_adaptor(none, sec_adaptor1, &final_sig, partial_sig, 2, 1) == 1); + + /** cleanup **/ + memset(&session, 0, sizeof(session)); + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); +} + +/* Initializes two sessions, one use the given parameters (session_id, + * nonce_commitments, etc.) except that `session_tmp` uses new signers with different + * public keys. The point of this test is to call `musig_session_get_public_nonce` + * with signers from `session_tmp` who have different public keys than the correct + * ones and return the resulting messagehash. This should not result in a different + * messagehash because the public keys of the signers are only used during session + * initialization. */ +int musig_state_machine_diff_signer_msghash_test(unsigned char *msghash, secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, const unsigned char * const *nonce_commitments, unsigned char *msg, secp256k1_pubkey *nonce_other, unsigned char *sk, unsigned char *session_id) { + secp256k1_musig_session session; + secp256k1_musig_session session_tmp; + unsigned char nonce_commitment[32]; + secp256k1_musig_session_signer_data signers[2]; + secp256k1_musig_session_signer_data signers_tmp[2]; + unsigned char sk_dummy[32]; + secp256k1_pubkey pks_tmp[2]; + secp256k1_pubkey combined_pk_tmp; + unsigned char pk_hash_tmp[32]; + secp256k1_pubkey nonce; + + /* Set up signers with different public keys */ + secp256k1_rand256(sk_dummy); + pks_tmp[0] = pks[0]; + CHECK(secp256k1_ec_pubkey_create(ctx, &pks_tmp[1], sk_dummy) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, NULL, &combined_pk_tmp, pk_hash_tmp, pks_tmp, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session_tmp, signers_tmp, nonce_commitment, session_id, msg, &combined_pk_tmp, pk_hash_tmp, 2, 0, sk_dummy) == 1); + + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + CHECK(memcmp(nonce_commitment, nonce_commitments[1], 32) == 0); + /* Call get_public_nonce with different signers than the signers the session was + * initialized with. */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session_tmp, signers, &nonce, nonce_commitments, 2) == 1); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers_tmp, &nonce, nonce_commitments, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + + return secp256k1_musig_compute_messagehash(ctx, msghash, &session); +} + +/* Creates a new session (with a different session id) and tries to use that session + * to combine nonces with given signers_other. This should fail, because the nonce + * commitments of signers_other do not match the nonce commitments the new session + * was initialized with. If do_test is 0, the correct signers are being used and + * therefore the function should return 1. */ +int musig_state_machine_diff_signers_combine_nonce_test(secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, unsigned char *msg, unsigned char *sk, secp256k1_musig_session_signer_data *signers_other, int do_test) { + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + secp256k1_musig_session_signer_data *signers_to_use; + unsigned char nonce_commitment[32]; + unsigned char session_id[32]; + secp256k1_pubkey nonce; + const unsigned char *ncs[2]; + + /* Initialize new signers */ + secp256k1_rand256(session_id); + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 1, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + secp256k1_musig_session_combine_nonces(ctx, &session, signers_other, 2, NULL, NULL); + if (do_test) { + signers_to_use = signers_other; + } else { + signers_to_use = signers; + } + return secp256k1_musig_session_combine_nonces(ctx, &session, signers_to_use, 2, NULL, NULL); +} + +/* Recreates a session with the given session_id, signers, pk, msg etc. parameters + * and tries to sign and verify the other signers partial signature. Both should fail + * if msg is NULL. */ +int musig_state_machine_missing_msg_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *sk, unsigned char *session_id, unsigned char *msg) { + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + unsigned char nonce_commitment[32]; + const unsigned char *ncs[2]; + secp256k1_pubkey nonce; + secp256k1_musig_partial_signature partial_sig; + int partial_sign, partial_verify; + + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + partial_sign = secp256k1_musig_partial_sign(ctx, &session, &partial_sig); + partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, &signers[0], partial_sig_other, &pks[0]); + if (msg != NULL) { + /* Return 1 if both succeeded */ + return partial_sign && partial_verify; + } + /* Return 0 if both failed */ + return partial_sign || partial_verify; +} + +/* Recreates a session with the given session_id, signers, pk, msg etc. parameters + * and tries to verify and combine partial sigs. If do_combine is 0, the + * combine_nonces step is left out. In that case verify and combine should fail and + * this function should return 0. */ +int musig_state_machine_missing_combine_test(secp256k1_pubkey *pks, secp256k1_pubkey *combined_pk, unsigned char *pk_hash, unsigned char *nonce_commitment_other, secp256k1_pubkey *nonce_other, secp256k1_musig_partial_signature *partial_sig_other, unsigned char *msg, unsigned char *sk, unsigned char *session_id, secp256k1_musig_partial_signature *partial_sig, int do_combine) { + secp256k1_musig_session session; + secp256k1_musig_session_signer_data signers[2]; + unsigned char nonce_commitment[32]; + const unsigned char *ncs[2]; + secp256k1_pubkey nonce; + secp256k1_musig_partial_signature partial_sigs[2]; + secp256k1_schnorrsig sig; + int partial_verify, sig_combine; + + CHECK(secp256k1_musig_session_initialize(ctx, &session, signers, nonce_commitment, session_id, msg, combined_pk, pk_hash, 2, 0, sk) == 1); + ncs[0] = nonce_commitment_other; + ncs[1] = nonce_commitment; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session, signers, &nonce, ncs, 2) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[0], nonce_other) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers[1], &nonce) == 1); + + partial_sigs[0] = *partial_sig_other; + partial_sigs[1] = *partial_sig; + if (do_combine != 0) { + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session, signers, 2, NULL, NULL) == 1); + } + partial_verify = secp256k1_musig_partial_sig_verify(ctx, &session, signers, partial_sig_other, &pks[0]); + sig_combine = secp256k1_musig_partial_sig_combine(ctx, &session, &sig, partial_sigs, 2); + if (do_combine != 0) { + /* Return 1 if both succeeded */ + return partial_verify && sig_combine; + } + /* Return 0 if both failed */ + return partial_verify || sig_combine; +} + +void musig_state_machine_tests(secp256k1_scratch_space *scratch) { + size_t i; + secp256k1_musig_session session[2]; + secp256k1_musig_session_signer_data signers0[2]; + secp256k1_musig_session_signer_data signers1[2]; + unsigned char nonce_commitment[2][32]; + unsigned char session_id[2][32]; + unsigned char msg[32]; + unsigned char sk[2][32]; + secp256k1_pubkey pk[2]; + secp256k1_pubkey combined_pk; + unsigned char pk_hash[32]; + secp256k1_pubkey nonce[2]; + const unsigned char *ncs[2]; + secp256k1_musig_partial_signature partial_sig[2]; + unsigned char msghash1[32]; + unsigned char msghash2[32]; + + /* Run state machine with the same objects twice to test that it's allowed to + * reinitialize session and session_signer_data. */ + for (i = 0; i < 2; i++) { + /* Setup */ + secp256k1_rand256(session_id[0]); + secp256k1_rand256(session_id[1]); + secp256k1_rand256(sk[0]); + secp256k1_rand256(sk[1]); + secp256k1_rand256(msg); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk[0]) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk[1]) == 1); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk, pk_hash, pk, 2) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[0], signers0, nonce_commitment[0], session_id[0], msg, &combined_pk, pk_hash, 2, 0, sk[0]) == 1); + CHECK(secp256k1_musig_session_initialize(ctx, &session[1], signers1, nonce_commitment[1], session_id[1], msg, &combined_pk, pk_hash, 2, 1, sk[1]) == 1); + + /* Set nonce commitments */ + ncs[0] = nonce_commitment[0]; + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); + /* Changing a nonce commitment is not okay */ + ncs[1] = (unsigned char*) "this isn't a nonce commitment..."; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 0); + /* Repeating with the same nonce commitments is okay */ + ncs[1] = nonce_commitment[1]; + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[0], signers0, &nonce[0], ncs, 2) == 1); + + /* Get nonce for signer 1 */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &session[1], signers1, &nonce[1], ncs, 2) == 1); + + /* Set nonces */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[0], &nonce[0]) == 1); + /* Can't set nonce that doesn't match nonce commitment */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[0]) == 0); + /* Set correct nonce */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers0[1], &nonce[1]) == 1); + + /* Combine nonces */ + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[0], signers0, 2, NULL, NULL) == 1); + /* Not everyone is present from signer 1's view */ + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 0); + /* Make everyone present */ + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[0], &nonce[0]) == 1); + CHECK(secp256k1_musig_set_nonce(ctx, &signers1[1], &nonce[1]) == 1); + + /* Can't combine nonces from signers of a different session */ + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 1) == 0); + CHECK(musig_state_machine_diff_signers_combine_nonce_test(&combined_pk, pk_hash, nonce_commitment[0], &nonce[0], msg, sk[1], signers1, 0) == 1); + + /* Partially sign */ + CHECK(secp256k1_musig_partial_sign(ctx, &session[0], &partial_sig[0]) == 1); + /* Can't verify or sign until nonce is combined */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 0); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 0); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &session[1], signers1, 2, NULL, NULL) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[0], &partial_sig[0], &pk[0]) == 1); + /* messagehash should be the same as a session whose get_public_nonce was called + * with different signers (i.e. they diff in public keys). This is because the + * public keys of the signers is set in stone when initializing the session. */ + CHECK(secp256k1_musig_compute_messagehash(ctx, msghash1, &session[1]) == 1); + CHECK(musig_state_machine_diff_signer_msghash_test(msghash2, pk, &combined_pk, pk_hash, ncs, msg, &nonce[0], sk[1], session_id[1]) == 1); + CHECK(memcmp(msghash1, msghash2, 32) == 0); + CHECK(secp256k1_musig_partial_sign(ctx, &session[1], &partial_sig[1]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[1], &pk[1]) == 1); + /* Wrong signature */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &session[1], &signers1[1], &partial_sig[0], &pk[1]) == 0); + /* Can't sign or verify until msg is set */ + CHECK(musig_state_machine_missing_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], sk[1], session_id[1], NULL) == 0); + CHECK(musig_state_machine_missing_msg_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], sk[1], session_id[1], msg) == 1); + + /* Can't verify and combine partial sigs until nonces are combined */ + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 0) == 0); + CHECK(musig_state_machine_missing_combine_test(pk, &combined_pk, pk_hash, nonce_commitment[0], &nonce[0], &partial_sig[0], msg, sk[1], session_id[1], &partial_sig[1], 1) == 1); + } +} + +void scriptless_atomic_swap(secp256k1_scratch_space *scratch) { + /* Throughout this test "a" and "b" refer to two hypothetical blockchains, + * while the indices 0 and 1 refer to the two signers. Here signer 0 is + * sending a-coins to signer 1, while signer 1 is sending b-coins to signer + * 0. Signer 0 produces the adaptor signatures. */ + secp256k1_schnorrsig final_sig_a; + secp256k1_schnorrsig final_sig_b; + secp256k1_musig_partial_signature partial_sig_a[2]; + secp256k1_musig_partial_signature partial_sig_b_adapted[2]; + secp256k1_musig_partial_signature partial_sig_b[2]; + unsigned char sec_adaptor[32]; + unsigned char sec_adaptor_extracted[32]; + secp256k1_pubkey pub_adaptor; + + unsigned char seckey_a[2][32]; + unsigned char seckey_b[2][32]; + secp256k1_pubkey pk_a[2]; + secp256k1_pubkey pk_b[2]; + unsigned char pk_hash_a[32]; + unsigned char pk_hash_b[32]; + secp256k1_pubkey combined_pk_a; + secp256k1_pubkey combined_pk_b; + secp256k1_musig_session musig_session_a[2]; + secp256k1_musig_session musig_session_b[2]; + unsigned char noncommit_a[2][32]; + unsigned char noncommit_b[2][32]; + const unsigned char *noncommit_a_ptr[2]; + const unsigned char *noncommit_b_ptr[2]; + secp256k1_pubkey pubnon_a[2]; + secp256k1_pubkey pubnon_b[2]; + int nonce_is_negated_a; + int nonce_is_negated_b; + secp256k1_musig_session_signer_data data_a[2]; + secp256k1_musig_session_signer_data data_b[2]; + + const unsigned char seed[32] = "still tired of choosing seeds..."; + const unsigned char msg32_a[32] = "this is the message blockchain a"; + const unsigned char msg32_b[32] = "this is the message blockchain b"; + + /* Step 1: key setup */ + secp256k1_rand256(seckey_a[0]); + secp256k1_rand256(seckey_a[1]); + secp256k1_rand256(seckey_b[0]); + secp256k1_rand256(seckey_b[1]); + secp256k1_rand256(sec_adaptor); + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[0], seckey_a[0])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_a[1], seckey_a[1])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[0], seckey_b[0])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk_b[1], seckey_b[1])); + CHECK(secp256k1_ec_pubkey_create(ctx, &pub_adaptor, sec_adaptor)); + + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_a, pk_hash_a, pk_a, 2)); + CHECK(secp256k1_musig_pubkey_combine(ctx, scratch, &combined_pk_b, pk_hash_b, pk_b, 2)); + + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[0], data_a, noncommit_a[0], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 0, seckey_a[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_a[1], data_a, noncommit_a[1], seed, msg32_a, &combined_pk_a, pk_hash_a, 2, 1, seckey_a[1])); + noncommit_a_ptr[0] = noncommit_a[0]; + noncommit_a_ptr[1] = noncommit_a[1]; + + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[0], data_b, noncommit_b[0], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 0, seckey_b[0])); + CHECK(secp256k1_musig_session_initialize(ctx, &musig_session_b[1], data_b, noncommit_b[1], seed, msg32_b, &combined_pk_b, pk_hash_b, 2, 1, seckey_b[1])); + noncommit_b_ptr[0] = noncommit_b[0]; + noncommit_b_ptr[1] = noncommit_b[1]; + + /* Step 2: Exchange nonces */ + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[0], data_a, &pubnon_a[0], noncommit_a_ptr, 2)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_a[1], data_a, &pubnon_a[1], noncommit_a_ptr, 2)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[0], data_b, &pubnon_b[0], noncommit_b_ptr, 2)); + CHECK(secp256k1_musig_session_get_public_nonce(ctx, &musig_session_b[1], data_b, &pubnon_b[1], noncommit_b_ptr, 2)); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[0], &pubnon_a[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_a[1], &pubnon_a[1])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[0], &pubnon_b[0])); + CHECK(secp256k1_musig_set_nonce(ctx, &data_b[1], &pubnon_b[1])); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[0], data_a, 2, &nonce_is_negated_a, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_a[1], data_a, 2, NULL, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[0], data_b, 2, &nonce_is_negated_b, &pub_adaptor)); + CHECK(secp256k1_musig_session_combine_nonces(ctx, &musig_session_b[1], data_b, 2, NULL, &pub_adaptor)); + + /* Step 3: Signer 0 produces partial signatures for both chains. */ + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[0], &partial_sig_a[0])); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[0], &partial_sig_b[0])); + + /* Step 4: Signer 1 receives partial signatures, verifies them and creates a + * partial signature to send B-coins to signer 0. */ + CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_a[1], data_a, &partial_sig_a[0], &pk_a[0]) == 1); + CHECK(secp256k1_musig_partial_sig_verify(ctx, &musig_session_b[1], data_b, &partial_sig_b[0], &pk_b[0]) == 1); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_b[1], &partial_sig_b[1])); + + /* Step 5: Signer 0 adapts its own partial signature and combines it with the + * partial signature from signer 1. This results in a complete signature which + * is broadcasted by signer 0 to take B-coins. */ + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_b_adapted[0], &partial_sig_b[0], sec_adaptor, nonce_is_negated_b)); + memcpy(&partial_sig_b_adapted[1], &partial_sig_b[1], sizeof(partial_sig_b_adapted[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_b[0], &final_sig_b, partial_sig_b_adapted, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_b, msg32_b, &combined_pk_b) == 1); + + /* Step 6: Signer 1 extracts adaptor from the published signature, applies it to + * other partial signature, and takes A-coins. */ + CHECK(secp256k1_musig_extract_secret_adaptor(ctx, sec_adaptor_extracted, &final_sig_b, partial_sig_b, 2, nonce_is_negated_b) == 1); + CHECK(memcmp(sec_adaptor_extracted, sec_adaptor, sizeof(sec_adaptor)) == 0); /* in real life we couldn't check this, of course */ + CHECK(secp256k1_musig_partial_sig_adapt(ctx, &partial_sig_a[0], &partial_sig_a[0], sec_adaptor_extracted, nonce_is_negated_a)); + CHECK(secp256k1_musig_partial_sign(ctx, &musig_session_a[1], &partial_sig_a[1])); + CHECK(secp256k1_musig_partial_sig_combine(ctx, &musig_session_a[1], &final_sig_a, partial_sig_a, 2) == 1); + CHECK(secp256k1_schnorrsig_verify(ctx, &final_sig_a, msg32_a, &combined_pk_a) == 1); +} + +/* Checks that hash initialized by secp256k1_musig_sha256_init_tagged has the + * expected state. */ +void sha256_tag_test(void) { + char tag[17] = "MuSig coefficient"; + secp256k1_sha256 sha; + secp256k1_sha256 sha_tagged; + unsigned char buf[32]; + unsigned char buf2[32]; + size_t i; + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, (unsigned char *) tag, 17); + secp256k1_sha256_finalize(&sha, buf); + /* buf = SHA256("MuSig coefficient") */ + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_write(&sha, buf, 32); + /* Is buffer fully consumed? */ + CHECK((sha.bytes & 0x3F) == 0); + + /* Compare with tagged SHA */ + secp256k1_musig_sha256_init_tagged(&sha_tagged); + for (i = 0; i < 8; i++) { + CHECK(sha_tagged.s[i] == sha.s[i]); + } + secp256k1_sha256_write(&sha, buf, 32); + secp256k1_sha256_write(&sha_tagged, buf, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_sha256_finalize(&sha_tagged, buf2); + CHECK(memcmp(buf, buf2, 32) == 0); +} + +void run_musig_tests(void) { + int i; + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + + musig_api_tests(scratch); + musig_state_machine_tests(scratch); + for (i = 0; i < count; i++) { + /* Run multiple times to ensure that the nonce is negated in some tests */ + scriptless_atomic_swap(scratch); + } + sha256_tag_test(); + + secp256k1_scratch_space_destroy(scratch); +} + +#endif + diff --git a/src/secp256k1/src/modules/schnorrsig/Makefile.am.include b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include new file mode 100644 index 000000000..0296c1299 --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/Makefile.am.include @@ -0,0 +1,9 @@ +include_HEADERS += include/secp256k1_schnorrsig.h +noinst_HEADERS += src/modules/schnorrsig/main_impl.h +noinst_HEADERS += src/modules/schnorrsig/tests_impl.h +if USE_BENCHMARK +noinst_PROGRAMS += bench_schnorrsig +bench_schnorrsig_SOURCES = src/bench_schnorrsig.c +bench_schnorrsig_LDADD = libsecp256k1.la $(SECP_LIBS) $(COMMON_LIB) +endif + diff --git a/src/secp256k1/src/modules/schnorrsig/main_impl.h b/src/secp256k1/src/modules/schnorrsig/main_impl.h new file mode 100644 index 000000000..f2b418159 --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/main_impl.h @@ -0,0 +1,339 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_MAIN_ +#define _SECP256K1_MODULE_SCHNORRSIG_MAIN_ + +#include "../../../include/secp256k1.h" +#include "../../../include/secp256k1_schnorrsig.h" +#include "hash.h" + +int secp256k1_schnorrsig_serialize(const secp256k1_context* ctx, unsigned char *out64, const secp256k1_schnorrsig* sig) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(out64 != NULL); + ARG_CHECK(sig != NULL); + memcpy(out64, sig->data, 64); + return 1; +} + +int secp256k1_schnorrsig_parse(const secp256k1_context* ctx, secp256k1_schnorrsig* sig, const unsigned char *in64) { + (void) ctx; + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(sig != NULL); + ARG_CHECK(in64 != NULL); + memcpy(sig->data, in64, 64); + return 1; +} + +int secp256k1_schnorrsig_sign(const secp256k1_context* ctx, secp256k1_schnorrsig *sig, int *nonce_is_negated, const unsigned char *msg32, const unsigned char *seckey, secp256k1_nonce_function noncefp, void *ndata) { + secp256k1_scalar x; + secp256k1_scalar e; + secp256k1_scalar k; + secp256k1_gej pkj; + secp256k1_gej rj; + secp256k1_ge pk; + secp256k1_ge r; + secp256k1_sha256 sha; + int overflow; + unsigned char buf[33]; + size_t buflen = sizeof(buf); + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_gen_context_is_built(&ctx->ecmult_gen_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(seckey != NULL); + + if (noncefp == NULL) { + noncefp = secp256k1_nonce_function_bipschnorr; + } + secp256k1_scalar_set_b32(&x, seckey, &overflow); + /* Fail if the secret key is invalid. */ + if (overflow || secp256k1_scalar_is_zero(&x)) { + memset(sig, 0, sizeof(*sig)); + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &pkj, &x); + secp256k1_ge_set_gej(&pk, &pkj); + + if (!noncefp(buf, msg32, seckey, NULL, (void*)ndata, 0)) { + return 0; + } + secp256k1_scalar_set_b32(&k, buf, NULL); + if (secp256k1_scalar_is_zero(&k)) { + return 0; + } + + secp256k1_ecmult_gen(&ctx->ecmult_gen_ctx, &rj, &k); + secp256k1_ge_set_gej(&r, &rj); + + if (nonce_is_negated != NULL) { + *nonce_is_negated = 0; + } + if (!secp256k1_fe_is_quad_var(&r.y)) { + secp256k1_scalar_negate(&k, &k); + if (nonce_is_negated != NULL) { + *nonce_is_negated = 1; + } + } + secp256k1_fe_normalize(&r.x); + secp256k1_fe_get_b32(&sig->data[0], &r.x); + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_eckey_pubkey_serialize(&pk, buf, &buflen, 1); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(&e, buf, NULL); + secp256k1_scalar_mul(&e, &e, &x); + secp256k1_scalar_add(&e, &e, &k); + + secp256k1_scalar_get_b32(&sig->data[32], &e); + secp256k1_scalar_clear(&k); + secp256k1_scalar_clear(&x); + + return 1; +} + +/* Helper function for verification and batch verification. + * Computes R = sG - eP. */ +static int secp256k1_schnorrsig_real_verify(const secp256k1_context* ctx, secp256k1_gej *rj, const secp256k1_scalar *s, const secp256k1_scalar *e, const secp256k1_pubkey *pk) { + secp256k1_scalar nege; + secp256k1_ge pkp; + secp256k1_gej pkj; + + secp256k1_scalar_negate(&nege, e); + + if (!secp256k1_pubkey_load(ctx, &pkp, pk)) { + return 0; + } + secp256k1_gej_set_ge(&pkj, &pkp); + + /* rj = s*G + (-e)*pkj */ + secp256k1_ecmult(&ctx->ecmult_ctx, rj, &pkj, &nege, s); + return 1; +} + +int secp256k1_schnorrsig_verify(const secp256k1_context* ctx, const secp256k1_schnorrsig *sig, const unsigned char *msg32, const secp256k1_pubkey *pk) { + secp256k1_scalar s; + secp256k1_scalar e; + secp256k1_gej rj; + secp256k1_fe rx; + secp256k1_sha256 sha; + unsigned char buf[33]; + size_t buflen = sizeof(buf); + int overflow; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + + if (!secp256k1_fe_set_b32(&rx, &sig->data[0])) { + return 0; + } + + secp256k1_scalar_set_b32(&s, &sig->data[32], &overflow); + if (overflow) { + return 0; + } + + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &sig->data[0], 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk, SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, msg32, 32); + secp256k1_sha256_finalize(&sha, buf); + secp256k1_scalar_set_b32(&e, buf, NULL); + + if (!secp256k1_schnorrsig_real_verify(ctx, &rj, &s, &e, pk) + || !secp256k1_gej_has_quad_y_var(&rj) /* fails if rj is infinity */ + || !secp256k1_gej_eq_x_var(&rx, &rj)) { + return 0; + } + + return 1; +} + +/* Data that is used by the batch verification ecmult callback */ +typedef struct { + const secp256k1_context *ctx; + /* Seed for the random number generator */ + unsigned char chacha_seed[32]; + /* Caches randomizers generated by the PRNG which returns two randomizers per call. Caching + * avoids having to call the PRNG twice as often. The very first randomizer will be set to 1 and + * the PRNG is called at every odd indexed schnorrsig to fill the cache. */ + secp256k1_scalar randomizer_cache[2]; + /* Signature, message, public key tuples to verify */ + const secp256k1_schnorrsig *const *sig; + const unsigned char *const *msg32; + const secp256k1_pubkey *const *pk; + size_t n_sigs; +} secp256k1_schnorrsig_verify_ecmult_context; + +/* Callback function which is called by ecmult_multi in order to convert the ecmult_context + * consisting of signature, message and public key tuples into scalars and points. */ +static int secp256k1_schnorrsig_verify_batch_ecmult_callback(secp256k1_scalar *sc, secp256k1_ge *pt, size_t idx, void *data) { + secp256k1_schnorrsig_verify_ecmult_context *ecmult_context = (secp256k1_schnorrsig_verify_ecmult_context *) data; + + if (idx % 4 == 2) { + /* Every idx corresponds to a (scalar,point)-tuple. So this callback is called with 4 + * consecutive tuples before we need to call the RNG for new randomizers: + * (-randomizer_cache[0], R1) + * (-randomizer_cache[0]*e1, P1) + * (-randomizer_cache[1], R2) + * (-randomizer_cache[1]*e2, P2) */ + secp256k1_scalar_chacha20(&ecmult_context->randomizer_cache[0], &ecmult_context->randomizer_cache[1], ecmult_context->chacha_seed, idx / 4); + } + + /* R */ + if (idx % 2 == 0) { + secp256k1_fe rx; + *sc = ecmult_context->randomizer_cache[(idx / 2) % 2]; + if (!secp256k1_fe_set_b32(&rx, &ecmult_context->sig[idx / 2]->data[0])) { + return 0; + } + if (!secp256k1_ge_set_xquad(pt, &rx)) { + return 0; + } + /* eP */ + } else { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256 sha; + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, &ecmult_context->sig[idx / 2]->data[0], 32); + secp256k1_ec_pubkey_serialize(ecmult_context->ctx, buf, &buflen, ecmult_context->pk[idx / 2], SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(&sha, buf, buflen); + secp256k1_sha256_write(&sha, ecmult_context->msg32[idx / 2], 32); + secp256k1_sha256_finalize(&sha, buf); + + secp256k1_scalar_set_b32(sc, buf, NULL); + secp256k1_scalar_mul(sc, sc, &ecmult_context->randomizer_cache[(idx / 2) % 2]); + + if (!secp256k1_pubkey_load(ecmult_context->ctx, pt, ecmult_context->pk[idx / 2])) { + return 0; + } + } + return 1; +} + +/** Helper function for batch verification. Hashes signature verification data into the + * randomization seed and initializes ecmult_context. + * + * Returns 1 if the randomizer was successfully initialized. + * + * Args: ctx: a secp256k1 context object + * Out: ecmult_context: context for batch_ecmult_callback + * In/Out sha: an initialized sha256 object which hashes the schnorrsig input in order to get a + * seed for the randomizer PRNG + * In: sig: array of signatures, or NULL if there are no signatures + * msg32: array of messages, or NULL if there are no signatures + * pk: array of public keys, or NULL if there are no signatures + * n_sigs: number of signatures in above arrays (must be 0 if they are NULL) + */ +int secp256k1_schnorrsig_verify_batch_init_randomizer(const secp256k1_context *ctx, secp256k1_schnorrsig_verify_ecmult_context *ecmult_context, secp256k1_sha256 *sha, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { + size_t i; + + if (n_sigs > 0) { + ARG_CHECK(sig != NULL); + ARG_CHECK(msg32 != NULL); + ARG_CHECK(pk != NULL); + } + + for (i = 0; i < n_sigs; i++) { + unsigned char buf[33]; + size_t buflen = sizeof(buf); + secp256k1_sha256_write(sha, sig[i]->data, 64); + secp256k1_sha256_write(sha, msg32[i], 32); + secp256k1_ec_pubkey_serialize(ctx, buf, &buflen, pk[i], SECP256K1_EC_COMPRESSED); + secp256k1_sha256_write(sha, buf, 32); + } + ecmult_context->ctx = ctx; + ecmult_context->sig = sig; + ecmult_context->msg32 = msg32; + ecmult_context->pk = pk; + ecmult_context->n_sigs = n_sigs; + + return 1; +} + +/** Helper function for batch verification. Sums the s part of all signatures multiplied by their + * randomizer. + * + * Returns 1 if s is successfully summed. + * + * In/Out: s: the s part of the input sigs is added to this s argument + * In: chacha_seed: PRNG seed for computing randomizers + * sig: array of signatures, or NULL if there are no signatures + * n_sigs: number of signatures in above array (must be 0 if they are NULL) + */ +int secp256k1_schnorrsig_verify_batch_sum_s(secp256k1_scalar *s, unsigned char *chacha_seed, const secp256k1_schnorrsig *const *sig, size_t n_sigs) { + secp256k1_scalar randomizer_cache[2]; + size_t i; + + secp256k1_scalar_set_int(&randomizer_cache[0], 1); + for (i = 0; i < n_sigs; i++) { + int overflow; + secp256k1_scalar term; + if (i % 2 == 1) { + secp256k1_scalar_chacha20(&randomizer_cache[0], &randomizer_cache[1], chacha_seed, i / 2); + } + + secp256k1_scalar_set_b32(&term, &sig[i]->data[32], &overflow); + if (overflow) { + return 0; + } + secp256k1_scalar_mul(&term, &term, &randomizer_cache[i % 2]); + secp256k1_scalar_add(s, s, &term); + } + return 1; +} + +/* schnorrsig batch verification. + * Seeds a random number generator with the inputs and derives a random number ai for every + * signature i. Fails if y-coordinate of any R is not a quadratic residue or if + * 0 != -(s1 + a2*s2 + ... + au*su)G + R1 + a2*R2 + ... + au*Ru + e1*P1 + (a2*e2)P2 + ... + (au*eu)Pu. */ +int secp256k1_schnorrsig_verify_batch(const secp256k1_context *ctx, secp256k1_scratch *scratch, const secp256k1_schnorrsig *const *sig, const unsigned char *const *msg32, const secp256k1_pubkey *const *pk, size_t n_sigs) { + secp256k1_schnorrsig_verify_ecmult_context ecmult_context; + secp256k1_sha256 sha; + secp256k1_scalar s; + secp256k1_gej rj; + + VERIFY_CHECK(ctx != NULL); + ARG_CHECK(secp256k1_ecmult_context_is_built(&ctx->ecmult_ctx)); + ARG_CHECK(scratch != NULL); + /* Check that n_sigs is less than half of the maximum size_t value. This is necessary because + * the number of points given to ecmult_multi is 2*n_sigs. */ + ARG_CHECK(n_sigs <= SIZE_MAX / 2); + /* Check that n_sigs is less than 2^31 to ensure the same behavior of this function on 32-bit + * and 64-bit platforms. */ + ARG_CHECK(n_sigs < (size_t)(1 << 31)); + + secp256k1_sha256_initialize(&sha); + if (!secp256k1_schnorrsig_verify_batch_init_randomizer(ctx, &ecmult_context, &sha, sig, msg32, pk, n_sigs)) { + return 0; + } + secp256k1_sha256_finalize(&sha, ecmult_context.chacha_seed); + secp256k1_scalar_set_int(&ecmult_context.randomizer_cache[0], 1); + + secp256k1_scalar_clear(&s); + if (!secp256k1_schnorrsig_verify_batch_sum_s(&s, ecmult_context.chacha_seed, sig, n_sigs)) { + return 0; + } + secp256k1_scalar_negate(&s, &s); + + return secp256k1_ecmult_multi_var(&ctx->ecmult_ctx, scratch, &rj, &s, secp256k1_schnorrsig_verify_batch_ecmult_callback, (void *) &ecmult_context, 2 * n_sigs) + && secp256k1_gej_is_infinity(&rj); +} + +#endif + diff --git a/src/secp256k1/src/modules/schnorrsig/tests_impl.h b/src/secp256k1/src/modules/schnorrsig/tests_impl.h new file mode 100644 index 000000000..de84992a9 --- /dev/null +++ b/src/secp256k1/src/modules/schnorrsig/tests_impl.h @@ -0,0 +1,727 @@ +/********************************************************************** + * Copyright (c) 2018 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_MODULE_SCHNORRSIG_TESTS_ +#define _SECP256K1_MODULE_SCHNORRSIG_TESTS_ + +#include "secp256k1_schnorrsig.h" + +void test_schnorrsig_serialize(void) { + secp256k1_schnorrsig sig; + unsigned char in[64]; + unsigned char out[64]; + + memset(in, 0x12, 64); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, in)); + CHECK(secp256k1_schnorrsig_serialize(ctx, out, &sig)); + CHECK(memcmp(in, out, 64) == 0); +} + +void test_schnorrsig_api(secp256k1_scratch_space *scratch) { + unsigned char sk1[32]; + unsigned char sk2[32]; + unsigned char sk3[32]; + unsigned char msg[32]; + unsigned char sig64[64]; + secp256k1_pubkey pk[3]; + secp256k1_schnorrsig sig; + const secp256k1_schnorrsig *sigptr = &sig; + const unsigned char *msgptr = msg; + const secp256k1_pubkey *pkptr = &pk[0]; + int nonce_is_negated; + + /** setup **/ + secp256k1_context *none = secp256k1_context_create(SECP256K1_CONTEXT_NONE); + secp256k1_context *sign = secp256k1_context_create(SECP256K1_CONTEXT_SIGN); + secp256k1_context *vrfy = secp256k1_context_create(SECP256K1_CONTEXT_VERIFY); + secp256k1_context *both = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + int ecount; + + secp256k1_context_set_error_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_error_callback(both, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(none, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(sign, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(vrfy, counting_illegal_callback_fn, &ecount); + secp256k1_context_set_illegal_callback(both, counting_illegal_callback_fn, &ecount); + + secp256k1_rand256(sk1); + secp256k1_rand256(sk2); + secp256k1_rand256(sk3); + secp256k1_rand256(msg); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[0], sk1) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[1], sk2) == 1); + CHECK(secp256k1_ec_pubkey_create(ctx, &pk[2], sk3) == 1); + + /** main test body **/ + ecount = 0; + CHECK(secp256k1_schnorrsig_sign(none, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_sign(vrfy, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, sk1, NULL, NULL) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_sign(sign, NULL, &nonce_is_negated, msg, sk1, NULL, NULL) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, NULL, msg, sk1, NULL, NULL) == 1); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, NULL, sk1, NULL, NULL) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_sign(sign, &sig, &nonce_is_negated, msg, NULL, NULL, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + CHECK(secp256k1_schnorrsig_serialize(none, sig64, &sig) == 1); + CHECK(ecount == 0); + CHECK(secp256k1_schnorrsig_serialize(none, NULL, &sig) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_serialize(none, sig64, NULL) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, &sig, sig64) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_parse(none, NULL, sig64) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_parse(none, &sig, NULL) == 0); + CHECK(ecount == 4); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify(none, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify(sign, &sig, msg, &pk[0]) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, &pk[0]) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify(vrfy, NULL, msg, &pk[0]) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, NULL, &pk[0]) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify(vrfy, &sig, msg, NULL) == 0); + CHECK(ecount == 5); + + ecount = 0; + CHECK(secp256k1_schnorrsig_verify_batch(none, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 1); + CHECK(secp256k1_schnorrsig_verify_batch(sign, scratch, &sigptr, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, NULL, NULL, 0) == 1); + CHECK(ecount == 2); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, NULL, &msgptr, &pkptr, 1) == 0); + CHECK(ecount == 3); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, NULL, &pkptr, 1) == 0); + CHECK(ecount == 4); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, NULL, 1) == 0); + CHECK(ecount == 5); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, (size_t)1 << (sizeof(size_t)*8-1)) == 0); + CHECK(ecount == 6); + CHECK(secp256k1_schnorrsig_verify_batch(vrfy, scratch, &sigptr, &msgptr, &pkptr, 1 << 31) == 0); + CHECK(ecount == 7); + + secp256k1_context_destroy(none); + secp256k1_context_destroy(sign); + secp256k1_context_destroy(vrfy); + secp256k1_context_destroy(both); +} + +/* Helper function for schnorrsig_bip_vectors + * Signs the message and checks that it's the same as expected_sig. */ +void test_schnorrsig_bip_vectors_check_signing(const unsigned char *sk, const unsigned char *pk_serialized, const unsigned char *msg, const unsigned char *expected_sig, const int expected_nonce_is_negated) { + secp256k1_schnorrsig sig; + unsigned char serialized_sig[64]; + secp256k1_pubkey pk; + int nonce_is_negated; + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, &nonce_is_negated, msg, sk, NULL, NULL)); + CHECK(nonce_is_negated == expected_nonce_is_negated); + CHECK(secp256k1_schnorrsig_serialize(ctx, serialized_sig, &sig)); + CHECK(memcmp(serialized_sig, expected_sig, 64) == 0); + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig, msg, &pk)); +} + +/* Helper function for schnorrsig_bip_vectors + * Checks that both verify and verify_batch return the same value as expected. */ +void test_schnorrsig_bip_vectors_check_verify(secp256k1_scratch_space *scratch, const unsigned char *pk_serialized, const unsigned char *msg32, const unsigned char *sig_serialized, int expected) { + const unsigned char *msg_arr[1]; + const secp256k1_schnorrsig *sig_arr[1]; + const secp256k1_pubkey *pk_arr[1]; + secp256k1_pubkey pk; + secp256k1_schnorrsig sig; + + CHECK(secp256k1_ec_pubkey_parse(ctx, &pk, pk_serialized, 33)); + CHECK(secp256k1_schnorrsig_parse(ctx, &sig, sig_serialized)); + + sig_arr[0] = &sig; + msg_arr[0] = msg32; + pk_arr[0] = &pk; + + CHECK(expected == secp256k1_schnorrsig_verify(ctx, &sig, msg32, &pk)); + CHECK(expected == secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); +} + +/* Test vectors according to BIP-schnorr + * (https://github.com/sipa/bips/blob/7f6a73e53c8bbcf2d008ea0546f76433e22094a8/bip-schnorr/test-vectors.csv). + */ +void test_schnorrsig_bip_vectors(secp256k1_scratch_space *scratch) { + { + /* Test vector 1 */ + const unsigned char sk1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }; + const unsigned char pk1[33] = { + 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, + 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, + 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98 + }; + const unsigned char msg1[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig1[64] = { + 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, + 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, + 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, + 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, + 0x70, 0x31, 0xA9, 0x88, 0x31, 0x85, 0x9D, 0xC3, + 0x4D, 0xFF, 0xEE, 0xDD, 0xA8, 0x68, 0x31, 0x84, + 0x2C, 0xCD, 0x00, 0x79, 0xE1, 0xF9, 0x2A, 0xF1, + 0x77, 0xF7, 0xF2, 0x2C, 0xC1, 0xDC, 0xED, 0x05 + }; + test_schnorrsig_bip_vectors_check_signing(sk1, pk1, msg1, sig1, 1); + test_schnorrsig_bip_vectors_check_verify(scratch, pk1, msg1, sig1, 1); + } + { + /* Test vector 2 */ + const unsigned char sk2[32] = { + 0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, + 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, + 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, + 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF + }; + const unsigned char pk2[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg2[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig2[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_signing(sk2, pk2, msg2, sig2, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk2, msg2, sig2, 1); + } + { + /* Test vector 3 */ + const unsigned char sk3[32] = { + 0xC9, 0x0F, 0xDA, 0xA2, 0x21, 0x68, 0xC2, 0x34, + 0xC4, 0xC6, 0x62, 0x8B, 0x80, 0xDC, 0x1C, 0xD1, + 0x29, 0x02, 0x4E, 0x08, 0x8A, 0x67, 0xCC, 0x74, + 0x02, 0x0B, 0xBE, 0xA6, 0x3B, 0x14, 0xE5, 0xC7 + }; + const unsigned char pk3[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg3[32] = { + 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig3[64] = { + 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, + 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, + 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, + 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, + 0x00, 0x88, 0x03, 0x71, 0xD0, 0x17, 0x66, 0x93, + 0x5B, 0x92, 0xD2, 0xAB, 0x4C, 0xD5, 0xC8, 0xA2, + 0xA5, 0x83, 0x7E, 0xC5, 0x7F, 0xED, 0x76, 0x60, + 0x77, 0x3A, 0x05, 0xF0, 0xDE, 0x14, 0x23, 0x80 + }; + test_schnorrsig_bip_vectors_check_signing(sk3, pk3, msg3, sig3, 0); + test_schnorrsig_bip_vectors_check_verify(scratch, pk3, msg3, sig3, 1); + } + { + /* Test vector 4 */ + const unsigned char pk4[33] = { + 0x03, 0xDE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, + 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, + 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, + 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, + 0x34 + }; + const unsigned char msg4[32] = { + 0x4D, 0xF3, 0xC3, 0xF6, 0x8F, 0xCC, 0x83, 0xB2, + 0x7E, 0x9D, 0x42, 0xC9, 0x04, 0x31, 0xA7, 0x24, + 0x99, 0xF1, 0x78, 0x75, 0xC8, 0x1A, 0x59, 0x9B, + 0x56, 0x6C, 0x98, 0x89, 0xB9, 0x69, 0x67, 0x03 + }; + const unsigned char sig4[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x3B, 0x78, 0xCE, 0x56, 0x3F, + 0x89, 0xA0, 0xED, 0x94, 0x14, 0xF5, 0xAA, 0x28, + 0xAD, 0x0D, 0x96, 0xD6, 0x79, 0x5F, 0x9C, 0x63, + 0x02, 0xA8, 0xDC, 0x32, 0xE6, 0x4E, 0x86, 0xA3, + 0x33, 0xF2, 0x0E, 0xF5, 0x6E, 0xAC, 0x9B, 0xA3, + 0x0B, 0x72, 0x46, 0xD6, 0xD2, 0x5E, 0x22, 0xAD, + 0xB8, 0xC6, 0xBE, 0x1A, 0xEB, 0x08, 0xD4, 0x9D + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk4, msg4, sig4, 1); + } + { + /* Test vector 5 */ + const unsigned char pk5[33] = { + 0x03, 0x1B, 0x84, 0xC5, 0x56, 0x7B, 0x12, 0x64, + 0x40, 0x99, 0x5D, 0x3E, 0xD5, 0xAA, 0xBA, 0x05, + 0x65, 0xD7, 0x1E, 0x18, 0x34, 0x60, 0x48, 0x19, + 0xFF, 0x9C, 0x17, 0xF5, 0xE9, 0xD5, 0xDD, 0x07, + 0x8F + }; + const unsigned char msg5[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig5[64] = { + 0x52, 0x81, 0x85, 0x79, 0xAC, 0xA5, 0x97, 0x67, + 0xE3, 0x29, 0x1D, 0x91, 0xB7, 0x6B, 0x63, 0x7B, + 0xEF, 0x06, 0x20, 0x83, 0x28, 0x49, 0x92, 0xF2, + 0xD9, 0x5F, 0x56, 0x4C, 0xA6, 0xCB, 0x4E, 0x35, + 0x30, 0xB1, 0xDA, 0x84, 0x9C, 0x8E, 0x83, 0x04, + 0xAD, 0xC0, 0xCF, 0xE8, 0x70, 0x66, 0x03, 0x34, + 0xB3, 0xCF, 0xC1, 0x8E, 0x82, 0x5E, 0xF1, 0xDB, + 0x34, 0xCF, 0xAE, 0x3D, 0xFC, 0x5D, 0x81, 0x87 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk5, msg5, sig5, 1); + } + { + /* Test vector 6 */ + const unsigned char pk6[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg6[32] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF + }; + const unsigned char sig6[64] = { + 0x57, 0x0D, 0xD4, 0xCA, 0x83, 0xD4, 0xE6, 0x31, + 0x7B, 0x8E, 0xE6, 0xBA, 0xE8, 0x34, 0x67, 0xA1, + 0xBF, 0x41, 0x9D, 0x07, 0x67, 0x12, 0x2D, 0xE4, + 0x09, 0x39, 0x44, 0x14, 0xB0, 0x50, 0x80, 0xDC, + 0xE9, 0xEE, 0x5F, 0x23, 0x7C, 0xBD, 0x10, 0x8E, + 0xAB, 0xAE, 0x1E, 0x37, 0x75, 0x9A, 0xE4, 0x7F, + 0x8E, 0x42, 0x03, 0xDA, 0x35, 0x32, 0xEB, 0x28, + 0xDB, 0x86, 0x0F, 0x33, 0xD6, 0x2D, 0x49, 0xBD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk6, msg6, sig6, 1); + } + { + /* Test vector 7 */ + const unsigned char pk7[33] = { + 0x03, 0xEE, 0xFD, 0xEA, 0x4C, 0xDB, 0x67, 0x77, + 0x50, 0xA4, 0x20, 0xFE, 0xE8, 0x07, 0xEA, 0xCF, + 0x21, 0xEB, 0x98, 0x98, 0xAE, 0x79, 0xB9, 0x76, + 0x87, 0x66, 0xE4, 0xFA, 0xA0, 0x4A, 0x2D, 0x4A, + 0x34 + }; + secp256k1_pubkey pk7_parsed; + /* No need to check the signature of the test vector as parsing the pubkey already fails */ + CHECK(!secp256k1_ec_pubkey_parse(ctx, &pk7_parsed, pk7, 33)); + } + { + /* Test vector 8 */ + const unsigned char pk8[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg8[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig8[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0xFA, 0x16, 0xAE, 0xE0, 0x66, 0x09, 0x28, 0x0A, + 0x19, 0xB6, 0x7A, 0x24, 0xE1, 0x97, 0x7E, 0x46, + 0x97, 0x71, 0x2B, 0x5F, 0xD2, 0x94, 0x39, 0x14, + 0xEC, 0xD5, 0xF7, 0x30, 0x90, 0x1B, 0x4A, 0xB7 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk8, msg8, sig8, 0); + } + { + /* Test vector 9 */ + const unsigned char pk9[33] = { + 0x03, 0xFA, 0xC2, 0x11, 0x4C, 0x2F, 0xBB, 0x09, + 0x15, 0x27, 0xEB, 0x7C, 0x64, 0xEC, 0xB1, 0x1F, + 0x80, 0x21, 0xCB, 0x45, 0xE8, 0xE7, 0x80, 0x9D, + 0x3C, 0x09, 0x38, 0xE4, 0xB8, 0xC0, 0xE5, 0xF8, + 0x4B + }; + const unsigned char msg9[32] = { + 0x5E, 0x2D, 0x58, 0xD8, 0xB3, 0xBC, 0xDF, 0x1A, + 0xBA, 0xDE, 0xC7, 0x82, 0x90, 0x54, 0xF9, 0x0D, + 0xDA, 0x98, 0x05, 0xAA, 0xB5, 0x6C, 0x77, 0x33, + 0x30, 0x24, 0xB9, 0xD0, 0xA5, 0x08, 0xB7, 0x5C + }; + const unsigned char sig9[64] = { + 0x00, 0xDA, 0x9B, 0x08, 0x17, 0x2A, 0x9B, 0x6F, + 0x04, 0x66, 0xA2, 0xDE, 0xFD, 0x81, 0x7F, 0x2D, + 0x7A, 0xB4, 0x37, 0xE0, 0xD2, 0x53, 0xCB, 0x53, + 0x95, 0xA9, 0x63, 0x86, 0x6B, 0x35, 0x74, 0xBE, + 0xD0, 0x92, 0xF9, 0xD8, 0x60, 0xF1, 0x77, 0x6A, + 0x1F, 0x74, 0x12, 0xAD, 0x8A, 0x1E, 0xB5, 0x0D, + 0xAC, 0xCC, 0x22, 0x2B, 0xC8, 0xC0, 0xE2, 0x6B, + 0x20, 0x56, 0xDF, 0x2F, 0x27, 0x3E, 0xFD, 0xEC + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk9, msg9, sig9, 0); + } + { + /* Test vector 10 */ + const unsigned char pk10[33] = { + 0x02, 0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, + 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, + 0x07, 0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, + 0xD9, 0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, + 0x98 + }; + const unsigned char msg10[32] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + const unsigned char sig10[64] = { + 0x78, 0x7A, 0x84, 0x8E, 0x71, 0x04, 0x3D, 0x28, + 0x0C, 0x50, 0x47, 0x0E, 0x8E, 0x15, 0x32, 0xB2, + 0xDD, 0x5D, 0x20, 0xEE, 0x91, 0x2A, 0x45, 0xDB, + 0xDD, 0x2B, 0xD1, 0xDF, 0xBF, 0x18, 0x7E, 0xF6, + 0x8F, 0xCE, 0x56, 0x77, 0xCE, 0x7A, 0x62, 0x3C, + 0xB2, 0x00, 0x11, 0x22, 0x57, 0x97, 0xCE, 0x7A, + 0x8D, 0xE1, 0xDC, 0x6C, 0xCD, 0x4F, 0x75, 0x4A, + 0x47, 0xDA, 0x6C, 0x60, 0x0E, 0x59, 0x54, 0x3C + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk10, msg10, sig10, 0); + } + { + /* Test vector 11 */ + const unsigned char pk11[33] = { + 0x03, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg11[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig11[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk11, msg11, sig11, 0); + } + { + /* Test vector 12 */ + const unsigned char pk12[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg12[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig12[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9E, 0x9D, 0x01, 0xAF, 0x98, 0x8B, 0x5C, 0xED, + 0xCE, 0x47, 0x22, 0x1B, 0xFA, 0x9B, 0x22, 0x27, + 0x21, 0xF3, 0xFA, 0x40, 0x89, 0x15, 0x44, 0x4A, + 0x4B, 0x48, 0x90, 0x21, 0xDB, 0x55, 0x77, 0x5F + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk12, msg12, sig12, 0); + } + { + /* Test vector 13 */ + const unsigned char pk13[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg13[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig13[64] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0xD3, 0x7D, 0xDF, 0x02, 0x54, 0x35, 0x18, 0x36, + 0xD8, 0x4B, 0x1B, 0xD6, 0xA7, 0x95, 0xFD, 0x5D, + 0x52, 0x30, 0x48, 0xF2, 0x98, 0xC4, 0x21, 0x4D, + 0x18, 0x7F, 0xE4, 0x89, 0x29, 0x47, 0xF7, 0x28 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk13, msg13, sig13, 0); + } + { + /* Test vector 14 */ + const unsigned char pk14[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg14[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x14, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig14[64] = { + 0x4A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk14, msg14, sig14, 0); + } + { + /* Test vector 15 */ + const unsigned char pk15[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg15[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig15[64] = { + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFC, 0x2F, + 0x1E, 0x51, 0xA2, 0x2C, 0xCE, 0xC3, 0x55, 0x99, + 0xB8, 0xF2, 0x66, 0x91, 0x22, 0x81, 0xF8, 0x36, + 0x5F, 0xFC, 0x2D, 0x03, 0x5A, 0x23, 0x04, 0x34, + 0xA1, 0xA6, 0x4D, 0xC5, 0x9F, 0x70, 0x13, 0xFD + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk15, msg15, sig15, 0); + } + { + /* Test vector 16 */ + const unsigned char pk16[33] = { + 0x02, 0xDF, 0xF1, 0xD7, 0x7F, 0x2A, 0x67, 0x1C, + 0x5F, 0x36, 0x18, 0x37, 0x26, 0xDB, 0x23, 0x41, + 0xBE, 0x58, 0xFE, 0xAE, 0x1D, 0xA2, 0xDE, 0xCE, + 0xD8, 0x43, 0x24, 0x0F, 0x7B, 0x50, 0x2B, 0xA6, + 0x59 + }; + const unsigned char msg16[32] = { + 0x24, 0x3F, 0x6A, 0x88, 0x85, 0xA3, 0x08, 0xD3, + 0x13, 0x19, 0x8A, 0x2E, 0x03, 0x70, 0x73, 0x44, + 0xA4, 0x09, 0x38, 0x22, 0x29, 0x9F, 0x31, 0xD0, + 0x08, 0x2E, 0xFA, 0x98, 0xEC, 0x4E, 0x6C, 0x89 + }; + const unsigned char sig16[64] = { + 0x2A, 0x29, 0x8D, 0xAC, 0xAE, 0x57, 0x39, 0x5A, + 0x15, 0xD0, 0x79, 0x5D, 0xDB, 0xFD, 0x1D, 0xCB, + 0x56, 0x4D, 0xA8, 0x2B, 0x0F, 0x26, 0x9B, 0xC7, + 0x0A, 0x74, 0xF8, 0x22, 0x04, 0x29, 0xBA, 0x1D, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, + 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, + 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41 + }; + test_schnorrsig_bip_vectors_check_verify(scratch, pk16, msg16, sig16, 0); + } +} + +/* Nonce function that returns constant 0 */ +static int nonce_function_failing(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + (void) nonce32; + return 0; +} + +/* Nonce function that sets nonce to 0 */ +static int nonce_function_0(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + (void) msg32; + (void) key32; + (void) algo16; + (void) data; + (void) counter; + + memset(nonce32, 0, 32); + return 1; +} + +void test_schnorrsig_sign(void) { + unsigned char sk[32]; + const unsigned char msg[32] = "this is a msg for a schnorrsig.."; + secp256k1_schnorrsig sig; + + memset(sk, 23, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 1); + + /* Overflowing secret key */ + memset(sk, 0xFF, sizeof(sk)); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, NULL, NULL) == 0); + memset(sk, 23, sizeof(sk)); + + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_failing, NULL) == 0); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig, NULL, msg, sk, nonce_function_0, NULL) == 0); +} + +#define N_SIGS 200 +/* Creates N_SIGS valid signatures and verifies them with verify and verify_batch. Then flips some + * bits and checks that verification now fails. */ +void test_schnorrsig_sign_verify(secp256k1_scratch_space *scratch) { + const unsigned char sk[32] = "shhhhhhhh! this key is a secret."; + unsigned char msg[N_SIGS][32]; + secp256k1_schnorrsig sig[N_SIGS]; + size_t i; + const secp256k1_schnorrsig *sig_arr[N_SIGS]; + const unsigned char *msg_arr[N_SIGS]; + const secp256k1_pubkey *pk_arr[N_SIGS]; + secp256k1_pubkey pk; + + CHECK(secp256k1_ec_pubkey_create(ctx, &pk, sk)); + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, NULL, NULL, NULL, 0)); + + for (i = 0; i < N_SIGS; i++) { + secp256k1_rand256(msg[i]); + CHECK(secp256k1_schnorrsig_sign(ctx, &sig[i], NULL, msg[i], sk, NULL, NULL)); + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[i], msg[i], &pk)); + sig_arr[i] = &sig[i]; + msg_arr[i] = msg[i]; + pk_arr[i] = &pk; + } + + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 1)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 2)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, N_SIGS)); + + { + /* Flip a few bits in the signature and in the message and check that + * verify and verify_batch fail */ + size_t sig_idx = secp256k1_rand_int(4); + size_t byte_idx = secp256k1_rand_int(32); + unsigned char xorbyte = secp256k1_rand_int(254)+1; + sig[sig_idx].data[byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + sig[sig_idx].data[32+byte_idx] ^= xorbyte; + + byte_idx = secp256k1_rand_int(32); + msg[sig_idx][byte_idx] ^= xorbyte; + CHECK(!secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(!secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + msg[sig_idx][byte_idx] ^= xorbyte; + + /* Check that above bitflips have been reversed correctly */ + CHECK(secp256k1_schnorrsig_verify(ctx, &sig[sig_idx], msg[sig_idx], &pk)); + CHECK(secp256k1_schnorrsig_verify_batch(ctx, scratch, sig_arr, msg_arr, pk_arr, 4)); + } +} +#undef N_SIGS + +void run_schnorrsig_tests(void) { + secp256k1_scratch_space *scratch = secp256k1_scratch_space_create(ctx, 1024 * 1024); + + test_schnorrsig_serialize(); + test_schnorrsig_api(scratch); + test_schnorrsig_bip_vectors(scratch); + test_schnorrsig_sign(); + test_schnorrsig_sign_verify(scratch); + + secp256k1_scratch_space_destroy(scratch); +} + +#endif + diff --git a/src/secp256k1/src/scalar.h b/src/secp256k1/src/scalar.h index 59304cb66..d83ccc22a 100644 --- a/src/secp256k1/src/scalar.h +++ b/src/secp256k1/src/scalar.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -104,3 +106,119 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); #endif /* SECP256K1_SCALAR_H */ + +#else + +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_H +#define SECP256K1_SCALAR_H + +#include "num.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32.h" +#else +#error "Please select scalar implementation" +#endif + +/** Clear a scalar to prevent the leak of sensitive data. */ +static void secp256k1_scalar_clear(secp256k1_scalar *r); + +/** Access bits from a scalar. All requested bits must belong to the same 32-bit limb. */ +static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Access bits from a scalar. Not constant time. */ +static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count); + +/** Set a scalar from a big endian byte array. */ +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *bin, int *overflow); + +/** Set a scalar to an unsigned integer. */ +static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v); + +/** Set a scalar to an unsigned 64-bit integer */ +static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v); + +/** Convert a scalar to a byte array. */ +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a); + +/** Add two scalars together (modulo the group order). Returns whether it overflowed. */ +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Conditionally add a power of two to a scalar. The result is not allowed to overflow. */ +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag); + +/** Multiply two scalars (modulo the group order). */ +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b); + +/** Shift a scalar right by some amount strictly between 0 and 16, returning + * the low bits that were shifted off */ +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n); + +/** Compute the square of a scalar (modulo the group order). */ +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order). */ +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the inverse of a scalar (modulo the group order), without constant-time guarantee. */ +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Compute the complement of a scalar (modulo the group order). */ +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a); + +/** Check whether a scalar equals zero. */ +static int secp256k1_scalar_is_zero(const secp256k1_scalar *a); + +/** Check whether a scalar equals one. */ +static int secp256k1_scalar_is_one(const secp256k1_scalar *a); + +/** Check whether a scalar, considered as an nonnegative integer, is even. */ +static int secp256k1_scalar_is_even(const secp256k1_scalar *a); + +/** Check whether a scalar is higher than the group order divided by 2. */ +static int secp256k1_scalar_is_high(const secp256k1_scalar *a); + +/** Conditionally negate a number, in constant time. + * Returns -1 if the number was negated, 1 otherwise */ +static int secp256k1_scalar_cond_negate(secp256k1_scalar *a, int flag); + +#ifndef USE_NUM_NONE +/** Convert a scalar to a number. */ +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a); + +/** Get the order of the group as a number. */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r); +#endif + +/** Compare two scalars. */ +static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b); + +#ifdef USE_ENDOMORPHISM +/** Find r1 and r2 such that r1+r2*2^128 = a. */ +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +/** Find r1 and r2 such that r1+r2*lambda = a, and r1 and r2 are maximum 128 bits long (see secp256k1_gej_mul_lambda). */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a); +#endif + +/** Multiply a and b (without taking the modulus!), divide by 2**shift, and round to the nearest integer. Shift must be at least 256. */ +static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift); + +/** Generate two scalars from a 32-byte seed and an integer using the chacha20 stream cipher */ +void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx); + +#endif /* SECP256K1_SCALAR_H */ +#endif diff --git a/src/secp256k1/src/scalar_4x64.h b/src/secp256k1/src/scalar_4x64.h index 19c7495d1..68096f2a9 100644 --- a/src/secp256k1/src/scalar_4x64.h +++ b/src/secp256k1/src/scalar_4x64.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -17,3 +19,26 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} #endif /* SECP256K1_SCALAR_REPR_H */ + +#else +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint64_t d[4]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{((uint64_t)(d1)) << 32 | (d0), ((uint64_t)(d3)) << 32 | (d2), ((uint64_t)(d5)) << 32 | (d4), ((uint64_t)(d7)) << 32 | (d6)}} + +#endif /* SECP256K1_SCALAR_REPR_H */ +#endif + diff --git a/src/secp256k1/src/scalar_4x64_impl.h b/src/secp256k1/src/scalar_4x64_impl.h index db1ebf94b..04f1da85d 100644 --- a/src/secp256k1/src/scalar_4x64_impl.h +++ b/src/secp256k1/src/scalar_4x64_impl.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2013, 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -947,3 +949,1058 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, } #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ + + +#else +/********************************************************************** + * Copyright (c) 2013, 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H + +#include "scalar.h" +#include + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint64_t)0xBFD25E8CD0364141ULL) +#define SECP256K1_N_1 ((uint64_t)0xBAAEDCE6AF48A03BULL) +#define SECP256K1_N_2 ((uint64_t)0xFFFFFFFFFFFFFFFEULL) +#define SECP256K1_N_3 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint64_t)0xDFE92F46681B20A0ULL) +#define SECP256K1_N_H_1 ((uint64_t)0x5D576E7357A4501DULL) +#define SECP256K1_N_H_2 ((uint64_t)0xFFFFFFFFFFFFFFFFULL) +#define SECP256K1_N_H_3 ((uint64_t)0x7FFFFFFFFFFFFFFFULL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 6 == offset >> 6); + return (a->d[offset >> 6] >> (offset & 0x3F)) & ((((uint64_t)1) << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 6 == offset >> 6) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 6) + 1 < 4); + return ((a->d[offset >> 6] >> (offset & 0x3F)) | (a->d[(offset >> 6) + 1] << (64 - (offset & 0x3F)))) & ((((uint64_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_3); /* No need for a > check. */ + no |= (a->d[2] < SECP256K1_N_2); + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1); + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, unsigned int overflow) { + uint128_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint128_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint64_t)r->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint128_t t = (uint128_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint128_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 6) > 3 makes this a noop */ + t = (uint128_t)r->d[0] + (((uint64_t)((bit >> 6) == 0)) << (bit & 0x3F)); + r->d[0] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[1] + (((uint64_t)((bit >> 6) == 1)) << (bit & 0x3F)); + r->d[1] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[2] + (((uint64_t)((bit >> 6) == 2)) << (bit & 0x3F)); + r->d[2] = t & 0xFFFFFFFFFFFFFFFFULL; t >>= 64; + t += (uint128_t)r->d[3] + (((uint64_t)((bit >> 6) == 3)) << (bit & 0x3F)); + r->d[3] = t & 0xFFFFFFFFFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 64) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint64_t)b32[31] | (uint64_t)b32[30] << 8 | (uint64_t)b32[29] << 16 | (uint64_t)b32[28] << 24 | (uint64_t)b32[27] << 32 | (uint64_t)b32[26] << 40 | (uint64_t)b32[25] << 48 | (uint64_t)b32[24] << 56; + r->d[1] = (uint64_t)b32[23] | (uint64_t)b32[22] << 8 | (uint64_t)b32[21] << 16 | (uint64_t)b32[20] << 24 | (uint64_t)b32[19] << 32 | (uint64_t)b32[18] << 40 | (uint64_t)b32[17] << 48 | (uint64_t)b32[16] << 56; + r->d[2] = (uint64_t)b32[15] | (uint64_t)b32[14] << 8 | (uint64_t)b32[13] << 16 | (uint64_t)b32[12] << 24 | (uint64_t)b32[11] << 32 | (uint64_t)b32[10] << 40 | (uint64_t)b32[9] << 48 | (uint64_t)b32[8] << 56; + r->d[3] = (uint64_t)b32[7] | (uint64_t)b32[6] << 8 | (uint64_t)b32[5] << 16 | (uint64_t)b32[4] << 24 | (uint64_t)b32[3] << 32 | (uint64_t)b32[2] << 40 | (uint64_t)b32[1] << 48 | (uint64_t)b32[0] << 56; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[3] >> 56; bin[1] = a->d[3] >> 48; bin[2] = a->d[3] >> 40; bin[3] = a->d[3] >> 32; bin[4] = a->d[3] >> 24; bin[5] = a->d[3] >> 16; bin[6] = a->d[3] >> 8; bin[7] = a->d[3]; + bin[8] = a->d[2] >> 56; bin[9] = a->d[2] >> 48; bin[10] = a->d[2] >> 40; bin[11] = a->d[2] >> 32; bin[12] = a->d[2] >> 24; bin[13] = a->d[2] >> 16; bin[14] = a->d[2] >> 8; bin[15] = a->d[2]; + bin[16] = a->d[1] >> 56; bin[17] = a->d[1] >> 48; bin[18] = a->d[1] >> 40; bin[19] = a->d[1] >> 32; bin[20] = a->d[1] >> 24; bin[21] = a->d[1] >> 16; bin[22] = a->d[1] >> 8; bin[23] = a->d[1]; + bin[24] = a->d[0] >> 56; bin[25] = a->d[0] >> 48; bin[26] = a->d[0] >> 40; bin[27] = a->d[0] >> 32; bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t nonzero = 0xFFFFFFFFFFFFFFFFULL * (secp256k1_scalar_is_zero(a) == 0); + uint128_t t = (uint128_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[3] < SECP256K1_N_H_3); + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; /* No need for a > check. */ + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint64_t mask = !flag - 1; + uint64_t nonzero = (secp256k1_scalar_is_zero(r) != 0) - 1; + uint128_t t = (uint128_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 64; + t += (uint128_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; + return 2 * (mask == 0) - 1; +} + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ +uint64_t tl, th; \ +{ \ +uint128_t t = (uint128_t)a * b; \ +th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ +tl = t; \ +} \ +c0 += tl; /* overflow is handled on the next line */ \ +th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ +c1 += th; /* overflow is handled on the next line */ \ +c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ +VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ +uint64_t tl, th; \ +{ \ +uint128_t t = (uint128_t)a * b; \ +th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ +tl = t; \ +} \ +c0 += tl; /* overflow is handled on the next line */ \ +th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ +c1 += th; /* never overflows by contract (verified in the next line) */ \ +VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ +uint64_t tl, th, th2, tl2; \ +{ \ +uint128_t t = (uint128_t)a * b; \ +th = t >> 64; /* at most 0xFFFFFFFFFFFFFFFE */ \ +tl = t; \ +} \ +th2 = th + th; /* at most 0xFFFFFFFFFFFFFFFE (in case th was 0x7FFFFFFFFFFFFFFF) */ \ +c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ +tl2 = tl + tl; /* at most 0xFFFFFFFFFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFFFFFFFFFF) */ \ +th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFFFFFFFFFF */ \ +c0 += tl2; /* overflow is handled on the next line */ \ +th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ +c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ +c1 += th2; /* overflow is handled on the next line */ \ +c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ +unsigned int over; \ +c0 += (a); /* overflow is handled on the next line */ \ +over = (c0 < (a)) ? 1 : 0; \ +c1 += over; /* overflow is handled on the next line */ \ +c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ +c0 += (a); /* overflow is handled on the next line */ \ +c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ +VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. */ +#define extract(n) { \ +(n) = c0; \ +c0 = c1; \ +c1 = c2; \ +c2 = 0; \ +} + +/** Extract the lowest 64 bits of (c0,c1,c2) into n, and left shift the number 64 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ +(n) = c0; \ +c0 = c1; \ +c1 = 0; \ +VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint64_t *l) { +#ifdef USE_ASM_X86_64 + /* Reduce 512 bits into 385. */ + uint64_t m0, m1, m2, m3, m4, m5, m6; + uint64_t p0, p1, p2, p3, p4; + uint64_t c; + + __asm__ __volatile__( + /* Preload. */ + "movq 32(%%rsi), %%r11\n" + "movq 40(%%rsi), %%r12\n" + "movq 48(%%rsi), %%r13\n" + "movq 56(%%rsi), %%r14\n" + /* Initialize r8,r9,r10 */ + "movq 0(%%rsi), %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += n0 * c0 */ + "movq %8, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract m0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += l1 */ + "addq 8(%%rsi), %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += n1 * c0 */ + "movq %8, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n0 * c1 */ + "movq %9, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract m1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += l2 */ + "addq 16(%%rsi), %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n2 * c0 */ + "movq %8, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n1 * c1 */ + "movq %9, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += n0 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract m2 */ + "movq %%r10, %q2\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += l3 */ + "addq 24(%%rsi), %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n3 * c0 */ + "movq %8, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n2 * c1 */ + "movq %9, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += n1 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + "adcq $0, %%r10\n" + /* extract m3 */ + "movq %%r8, %q3\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += n3 * c1 */ + "movq %9, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += n2 */ + "addq %%r13, %%r9\n" + "adcq $0, %%r10\n" + "adcq $0, %%r8\n" + /* extract m4 */ + "movq %%r9, %q4\n" + /* (r10,r8) += n3 */ + "addq %%r14, %%r10\n" + "adcq $0, %%r8\n" + /* extract m5 */ + "movq %%r10, %q5\n" + /* extract m6 */ + "movq %%r8, %q6\n" + : "=g"(m0), "=g"(m1), "=g"(m2), "=g"(m3), "=g"(m4), "=g"(m5), "=g"(m6) + : "S"(l), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc"); + + /* Reduce 385 bits into 258. */ + __asm__ __volatile__( + /* Preload */ + "movq %q9, %%r11\n" + "movq %q10, %%r12\n" + "movq %q11, %%r13\n" + /* Initialize (r8,r9,r10) */ + "movq %q5, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9) += m4 * c0 */ + "movq %12, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* extract p0 */ + "movq %%r8, %q0\n" + "xorq %%r8, %%r8\n" + /* (r9,r10) += m1 */ + "addq %q6, %%r9\n" + "adcq $0, %%r10\n" + /* (r9,r10,r8) += m5 * c0 */ + "movq %12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += m4 * c1 */ + "movq %13, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* extract p1 */ + "movq %%r9, %q1\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += m2 */ + "addq %q7, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m6 * c0 */ + "movq %12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m5 * c1 */ + "movq %13, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += m4 */ + "addq %%r11, %%r10\n" + "adcq $0, %%r8\n" + "adcq $0, %%r9\n" + /* extract p2 */ + "movq %%r10, %q2\n" + /* (r8,r9) += m3 */ + "addq %q8, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += m6 * c1 */ + "movq %13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* (r8,r9) += m5 */ + "addq %%r12, %%r8\n" + "adcq $0, %%r9\n" + /* extract p3 */ + "movq %%r8, %q3\n" + /* (r9) += m6 */ + "addq %%r13, %%r9\n" + /* extract p4 */ + "movq %%r9, %q4\n" + : "=&g"(p0), "=&g"(p1), "=&g"(p2), "=g"(p3), "=g"(p4) + : "g"(m0), "g"(m1), "g"(m2), "g"(m3), "g"(m4), "g"(m5), "g"(m6), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "cc"); + + /* Reduce 258 bits into 256. */ + __asm__ __volatile__( + /* Preload */ + "movq %q5, %%r10\n" + /* (rax,rdx) = p4 * c0 */ + "movq %7, %%rax\n" + "mulq %%r10\n" + /* (rax,rdx) += p0 */ + "addq %q1, %%rax\n" + "adcq $0, %%rdx\n" + /* extract r0 */ + "movq %%rax, 0(%q6)\n" + /* Move to (r8,r9) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p1 */ + "addq %q2, %%r8\n" + "adcq $0, %%r9\n" + /* (r8,r9) += p4 * c1 */ + "movq %8, %%rax\n" + "mulq %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + /* Extract r1 */ + "movq %%r8, 8(%q6)\n" + "xorq %%r8, %%r8\n" + /* (r9,r8) += p4 */ + "addq %%r10, %%r9\n" + "adcq $0, %%r8\n" + /* (r9,r8) += p2 */ + "addq %q3, %%r9\n" + "adcq $0, %%r8\n" + /* Extract r2 */ + "movq %%r9, 16(%q6)\n" + "xorq %%r9, %%r9\n" + /* (r8,r9) += p3 */ + "addq %q4, %%r8\n" + "adcq $0, %%r9\n" + /* Extract r3 */ + "movq %%r8, 24(%q6)\n" + /* Extract c */ + "movq %%r9, %q0\n" + : "=g"(c) + : "g"(p0), "g"(p1), "g"(p2), "g"(p3), "g"(p4), "D"(r), "n"(SECP256K1_N_C_0), "n"(SECP256K1_N_C_1) + : "rax", "rdx", "r8", "r9", "r10", "cc", "memory"); +#else + uint128_t c; + uint64_t c0, c1, c2; + uint64_t n0 = l[4], n1 = l[5], n2 = l[6], n3 = l[7]; + uint64_t m0, m1, m2, m3, m4, m5; + uint32_t m6; + uint64_t p0, p1, p2, p3; + uint32_t p4; + + /* Reduce 512 bits into 385. */ + /* m[0..6] = l[0..3] + n[0..3] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + sumadd(n0); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + sumadd(n1); + extract(m3); + muladd(n3, SECP256K1_N_C_1); + sumadd(n2); + extract(m4); + sumadd_fast(n3); + extract_fast(m5); + VERIFY_CHECK(c0 <= 1); + m6 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..4] = m[0..3] + m[4..6] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m4, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m5, SECP256K1_N_C_0); + muladd(m4, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m6, SECP256K1_N_C_0); + muladd(m5, SECP256K1_N_C_1); + sumadd(m4); + extract(p2); + sumadd_fast(m3); + muladd_fast(m6, SECP256K1_N_C_1); + sumadd_fast(m5); + extract_fast(p3); + p4 = c0 + m6; + VERIFY_CHECK(p4 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..3] = p[0..3] + p[4] * SECP256K1_N_C. */ + c = p0 + (uint128_t)SECP256K1_N_C_0 * p4; + r->d[0] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p1 + (uint128_t)SECP256K1_N_C_1 * p4; + r->d[1] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p2 + (uint128_t)p4; + r->d[2] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; + c += p3; + r->d[3] = c & 0xFFFFFFFFFFFFFFFFULL; c >>= 64; +#endif + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint64_t l[8], const secp256k1_scalar *a, const secp256k1_scalar *b) { +#ifdef USE_ASM_X86_64 + const uint64_t *pb = b->d; + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r15\n" + "movq 8(%%rdi), %%rbx\n" + "movq 16(%%rdi), %%rcx\n" + "movq 0(%%rdx), %%r11\n" + "movq 8(%%rdx), %%r12\n" + "movq 16(%%rdx), %%r13\n" + "movq 24(%%rdx), %%r14\n" + /* (rax,rdx) = a0 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a0 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a1 * b0 */ + "movq %%rbx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a0 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * b1 */ + "movq %%rbx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a2 * b0 */ + "movq %%rcx, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += a0 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Preload a3 */ + "movq 24(%%rdi), %%r15\n" + /* (r10,r8,r9) += a1 * b2 */ + "movq %%rbx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a2 * b1 */ + "movq %%rcx, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += a3 * b0 */ + "movq %%r15, %%rax\n" + "mulq %%r11\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += a1 * b3 */ + "movq %%rbx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * b2 */ + "movq %%rcx, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a3 * b1 */ + "movq %%r15, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += a2 * b3 */ + "movq %%rcx, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a3 * b2 */ + "movq %%r15, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * b3 */ + "movq %%r15, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : "+d"(pb) + : "S"(l), "D"(a->d) + : "rax", "rbx", "rcx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + extract(l[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + extract(l[5]); + muladd_fast(a->d[3], b->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +static void secp256k1_scalar_sqr_512(uint64_t l[8], const secp256k1_scalar *a) { +#ifdef USE_ASM_X86_64 + __asm__ __volatile__( + /* Preload */ + "movq 0(%%rdi), %%r11\n" + "movq 8(%%rdi), %%r12\n" + "movq 16(%%rdi), %%r13\n" + "movq 24(%%rdi), %%r14\n" + /* (rax,rdx) = a0 * a0 */ + "movq %%r11, %%rax\n" + "mulq %%r11\n" + /* Extract l0 */ + "movq %%rax, 0(%%rsi)\n" + /* (r8,r9,r10) = (rdx,0) */ + "movq %%rdx, %%r8\n" + "xorq %%r9, %%r9\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a0 * a1 */ + "movq %%r11, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l1 */ + "movq %%r8, 8(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a0 * a2 */ + "movq %%r11, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* (r9,r10,r8) += a1 * a1 */ + "movq %%r12, %%rax\n" + "mulq %%r12\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l2 */ + "movq %%r9, 16(%%rsi)\n" + "xorq %%r9, %%r9\n" + /* (r10,r8,r9) += 2 * a0 * a3 */ + "movq %%r11, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* (r10,r8,r9) += 2 * a1 * a2 */ + "movq %%r12, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + "adcq $0, %%r9\n" + /* Extract l3 */ + "movq %%r10, 24(%%rsi)\n" + "xorq %%r10, %%r10\n" + /* (r8,r9,r10) += 2 * a1 * a3 */ + "movq %%r12, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* (r8,r9,r10) += a2 * a2 */ + "movq %%r13, %%rax\n" + "mulq %%r13\n" + "addq %%rax, %%r8\n" + "adcq %%rdx, %%r9\n" + "adcq $0, %%r10\n" + /* Extract l4 */ + "movq %%r8, 32(%%rsi)\n" + "xorq %%r8, %%r8\n" + /* (r9,r10,r8) += 2 * a2 * a3 */ + "movq %%r13, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + "addq %%rax, %%r9\n" + "adcq %%rdx, %%r10\n" + "adcq $0, %%r8\n" + /* Extract l5 */ + "movq %%r9, 40(%%rsi)\n" + /* (r10,r8) += a3 * a3 */ + "movq %%r14, %%rax\n" + "mulq %%r14\n" + "addq %%rax, %%r10\n" + "adcq %%rdx, %%r8\n" + /* Extract l6 */ + "movq %%r10, 48(%%rsi)\n" + /* Extract l7 */ + "movq %%r8, 56(%%rsi)\n" + : + : "S"(l), "D"(a->d) + : "rax", "rdx", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "cc", "memory"); +#else + /* 160 bit accumulator. */ + uint64_t c0 = 0, c1 = 0; + uint32_t c2 = 0; + + /* l[0..7] = a[0..3] * b[0..3]. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd_fast(a->d[3], a->d[3]); + extract_fast(l[6]); + VERIFY_CHECK(c1 == 0); + l[7] = c0; +#endif +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint64_t l[8]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (64 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (64 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (64 - n)); + r->d[3] = (r->d[3] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint64_t l[8]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = 0; + r1->d[3] = 0; + r2->d[0] = a->d[2]; + r2->d[1] = a->d[3]; + r2->d[2] = 0; + r2->d[3] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint64_t l[8]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 6; + shiftlow = shift & 0x3F; + shifthigh = 64 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 448 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 384 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 320 ? (l[3 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 6] >> ((shift - 1) & 0x3f)) & 1); +} + +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ +a += b; d = ROTL32(d ^ a, 16); \ +c += d; b = ROTL32(b ^ c, 12); \ +a += b; d = ROTL32(d ^ a, 8); \ +c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define BE32(p) (p) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define LE32(p) (p) +#endif + +void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[3] = LE32((uint64_t) x0) << 32 | LE32(x1); + r1->d[2] = LE32((uint64_t) x2) << 32 | LE32(x3); + r1->d[1] = LE32((uint64_t) x4) << 32 | LE32(x5); + r1->d[0] = LE32((uint64_t) x6) << 32 | LE32(x7); + r2->d[3] = LE32((uint64_t) x8) << 32 | LE32(x9); + r2->d[2] = LE32((uint64_t) x10) << 32 | LE32(x11); + r2->d[1] = LE32((uint64_t) x12) << 32 | LE32(x13); + r2->d[0] = LE32((uint64_t) x14) << 32 | LE32(x15); + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef BE32 +#undef LE32 + +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ + +#endif + diff --git a/src/secp256k1/src/scalar_8x32.h b/src/secp256k1/src/scalar_8x32.h index 2c9a348e2..8a630ac40 100644 --- a/src/secp256k1/src/scalar_8x32.h +++ b/src/secp256k1/src/scalar_8x32.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -17,3 +19,25 @@ typedef struct { #define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} #endif /* SECP256K1_SCALAR_REPR_H */ + +#else +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef struct { + uint32_t d[8]; +} secp256k1_scalar; + +#define SECP256K1_SCALAR_CONST(d7, d6, d5, d4, d3, d2, d1, d0) {{(d0), (d1), (d2), (d3), (d4), (d5), (d6), (d7)}} + +#endif /* SECP256K1_SCALAR_REPR_H */ +#endif diff --git a/src/secp256k1/src/scalar_8x32_impl.h b/src/secp256k1/src/scalar_8x32_impl.h index 4f9ed61fe..f1429b162 100644 --- a/src/secp256k1/src/scalar_8x32_impl.h +++ b/src/secp256k1/src/scalar_8x32_impl.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -719,3 +721,839 @@ SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, } #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ + +#else +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H + +#include + +/* Limbs of the secp256k1 order. */ +#define SECP256K1_N_0 ((uint32_t)0xD0364141UL) +#define SECP256K1_N_1 ((uint32_t)0xBFD25E8CUL) +#define SECP256K1_N_2 ((uint32_t)0xAF48A03BUL) +#define SECP256K1_N_3 ((uint32_t)0xBAAEDCE6UL) +#define SECP256K1_N_4 ((uint32_t)0xFFFFFFFEUL) +#define SECP256K1_N_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_7 ((uint32_t)0xFFFFFFFFUL) + +/* Limbs of 2^256 minus the secp256k1 order. */ +#define SECP256K1_N_C_0 (~SECP256K1_N_0 + 1) +#define SECP256K1_N_C_1 (~SECP256K1_N_1) +#define SECP256K1_N_C_2 (~SECP256K1_N_2) +#define SECP256K1_N_C_3 (~SECP256K1_N_3) +#define SECP256K1_N_C_4 (1) + +/* Limbs of half the secp256k1 order. */ +#define SECP256K1_N_H_0 ((uint32_t)0x681B20A0UL) +#define SECP256K1_N_H_1 ((uint32_t)0xDFE92F46UL) +#define SECP256K1_N_H_2 ((uint32_t)0x57A4501DUL) +#define SECP256K1_N_H_3 ((uint32_t)0x5D576E73UL) +#define SECP256K1_N_H_4 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_5 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_6 ((uint32_t)0xFFFFFFFFUL) +#define SECP256K1_N_H_7 ((uint32_t)0x7FFFFFFFUL) + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { + r->d[0] = 0; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { + r->d[0] = v; + r->d[1] = 0; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { + r->d[0] = v; + r->d[1] = v >> 32; + r->d[2] = 0; + r->d[3] = 0; + r->d[4] = 0; + r->d[5] = 0; + r->d[6] = 0; + r->d[7] = 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK((offset + count - 1) >> 5 == offset >> 5); + return (a->d[offset >> 5] >> (offset & 0x1F)) & ((1 << count) - 1); +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + VERIFY_CHECK(count < 32); + VERIFY_CHECK(offset + count <= 256); + if ((offset + count - 1) >> 5 == offset >> 5) { + return secp256k1_scalar_get_bits(a, offset, count); + } else { + VERIFY_CHECK((offset >> 5) + 1 < 8); + return ((a->d[offset >> 5] >> (offset & 0x1F)) | (a->d[(offset >> 5) + 1] << (32 - (offset & 0x1F)))) & ((((uint32_t)1) << count) - 1); + } +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_7); /* No need for a > check. */ + no |= (a->d[6] < SECP256K1_N_6); /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_5); /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_4); + yes |= (a->d[4] > SECP256K1_N_4) & ~no; + no |= (a->d[3] < SECP256K1_N_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_3) & ~no; + no |= (a->d[2] < SECP256K1_N_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_2) & ~no; + no |= (a->d[1] < SECP256K1_N_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_1) & ~no; + yes |= (a->d[0] >= SECP256K1_N_0) & ~no; + return yes; +} + +SECP256K1_INLINE static int secp256k1_scalar_reduce(secp256k1_scalar *r, uint32_t overflow) { + uint64_t t; + VERIFY_CHECK(overflow <= 1); + t = (uint64_t)r->d[0] + overflow * SECP256K1_N_C_0; + r->d[0] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[1] + overflow * SECP256K1_N_C_1; + r->d[1] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[2] + overflow * SECP256K1_N_C_2; + r->d[2] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[3] + overflow * SECP256K1_N_C_3; + r->d[3] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[4] + overflow * SECP256K1_N_C_4; + r->d[4] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[5]; + r->d[5] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[6]; + r->d[6] = t & 0xFFFFFFFFUL; t >>= 32; + t += (uint64_t)r->d[7]; + r->d[7] = t & 0xFFFFFFFFUL; + return overflow; +} + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + int overflow; + uint64_t t = (uint64_t)a->d[0] + b->d[0]; + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[1] + b->d[1]; + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[2] + b->d[2]; + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[3] + b->d[3]; + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[4] + b->d[4]; + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[5] + b->d[5]; + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[6] + b->d[6]; + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)a->d[7] + b->d[7]; + r->d[7] = t & 0xFFFFFFFFULL; t >>= 32; + overflow = t + secp256k1_scalar_check_overflow(r); + VERIFY_CHECK(overflow == 0 || overflow == 1); + secp256k1_scalar_reduce(r, overflow); + return overflow; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + uint64_t t; + VERIFY_CHECK(bit < 256); + bit += ((uint32_t) flag - 1) & 0x100; /* forcing (bit >> 5) > 7 makes this a noop */ + t = (uint64_t)r->d[0] + (((uint32_t)((bit >> 5) == 0)) << (bit & 0x1F)); + r->d[0] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[1] + (((uint32_t)((bit >> 5) == 1)) << (bit & 0x1F)); + r->d[1] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[2] + (((uint32_t)((bit >> 5) == 2)) << (bit & 0x1F)); + r->d[2] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[3] + (((uint32_t)((bit >> 5) == 3)) << (bit & 0x1F)); + r->d[3] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[4] + (((uint32_t)((bit >> 5) == 4)) << (bit & 0x1F)); + r->d[4] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[5] + (((uint32_t)((bit >> 5) == 5)) << (bit & 0x1F)); + r->d[5] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[6] + (((uint32_t)((bit >> 5) == 6)) << (bit & 0x1F)); + r->d[6] = t & 0xFFFFFFFFULL; t >>= 32; + t += (uint64_t)r->d[7] + (((uint32_t)((bit >> 5) == 7)) << (bit & 0x1F)); + r->d[7] = t & 0xFFFFFFFFULL; +#ifdef VERIFY + VERIFY_CHECK((t >> 32) == 0); + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + int over; + r->d[0] = (uint32_t)b32[31] | (uint32_t)b32[30] << 8 | (uint32_t)b32[29] << 16 | (uint32_t)b32[28] << 24; + r->d[1] = (uint32_t)b32[27] | (uint32_t)b32[26] << 8 | (uint32_t)b32[25] << 16 | (uint32_t)b32[24] << 24; + r->d[2] = (uint32_t)b32[23] | (uint32_t)b32[22] << 8 | (uint32_t)b32[21] << 16 | (uint32_t)b32[20] << 24; + r->d[3] = (uint32_t)b32[19] | (uint32_t)b32[18] << 8 | (uint32_t)b32[17] << 16 | (uint32_t)b32[16] << 24; + r->d[4] = (uint32_t)b32[15] | (uint32_t)b32[14] << 8 | (uint32_t)b32[13] << 16 | (uint32_t)b32[12] << 24; + r->d[5] = (uint32_t)b32[11] | (uint32_t)b32[10] << 8 | (uint32_t)b32[9] << 16 | (uint32_t)b32[8] << 24; + r->d[6] = (uint32_t)b32[7] | (uint32_t)b32[6] << 8 | (uint32_t)b32[5] << 16 | (uint32_t)b32[4] << 24; + r->d[7] = (uint32_t)b32[3] | (uint32_t)b32[2] << 8 | (uint32_t)b32[1] << 16 | (uint32_t)b32[0] << 24; + over = secp256k1_scalar_reduce(r, secp256k1_scalar_check_overflow(r)); + if (overflow) { + *overflow = over; + } +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + bin[0] = a->d[7] >> 24; bin[1] = a->d[7] >> 16; bin[2] = a->d[7] >> 8; bin[3] = a->d[7]; + bin[4] = a->d[6] >> 24; bin[5] = a->d[6] >> 16; bin[6] = a->d[6] >> 8; bin[7] = a->d[6]; + bin[8] = a->d[5] >> 24; bin[9] = a->d[5] >> 16; bin[10] = a->d[5] >> 8; bin[11] = a->d[5]; + bin[12] = a->d[4] >> 24; bin[13] = a->d[4] >> 16; bin[14] = a->d[4] >> 8; bin[15] = a->d[4]; + bin[16] = a->d[3] >> 24; bin[17] = a->d[3] >> 16; bin[18] = a->d[3] >> 8; bin[19] = a->d[3]; + bin[20] = a->d[2] >> 24; bin[21] = a->d[2] >> 16; bin[22] = a->d[2] >> 8; bin[23] = a->d[2]; + bin[24] = a->d[1] >> 24; bin[25] = a->d[1] >> 16; bin[26] = a->d[1] >> 8; bin[27] = a->d[1]; + bin[28] = a->d[0] >> 24; bin[29] = a->d[0] >> 16; bin[30] = a->d[0] >> 8; bin[31] = a->d[0]; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return (a->d[0] | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(a) == 0); + uint64_t t = (uint64_t)(~a->d[0]) + SECP256K1_N_0 + 1; + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[1]) + SECP256K1_N_1; + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[2]) + SECP256K1_N_2; + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[3]) + SECP256K1_N_3; + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[4]) + SECP256K1_N_4; + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[5]) + SECP256K1_N_5; + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[6]) + SECP256K1_N_6; + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(~a->d[7]) + SECP256K1_N_7; + r->d[7] = t & nonzero; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return ((a->d[0] ^ 1) | a->d[1] | a->d[2] | a->d[3] | a->d[4] | a->d[5] | a->d[6] | a->d[7]) == 0; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + int yes = 0; + int no = 0; + no |= (a->d[7] < SECP256K1_N_H_7); + yes |= (a->d[7] > SECP256K1_N_H_7) & ~no; + no |= (a->d[6] < SECP256K1_N_H_6) & ~yes; /* No need for a > check. */ + no |= (a->d[5] < SECP256K1_N_H_5) & ~yes; /* No need for a > check. */ + no |= (a->d[4] < SECP256K1_N_H_4) & ~yes; /* No need for a > check. */ + no |= (a->d[3] < SECP256K1_N_H_3) & ~yes; + yes |= (a->d[3] > SECP256K1_N_H_3) & ~no; + no |= (a->d[2] < SECP256K1_N_H_2) & ~yes; + yes |= (a->d[2] > SECP256K1_N_H_2) & ~no; + no |= (a->d[1] < SECP256K1_N_H_1) & ~yes; + yes |= (a->d[1] > SECP256K1_N_H_1) & ~no; + yes |= (a->d[0] > SECP256K1_N_H_0) & ~no; + return yes; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + /* If we are flag = 0, mask = 00...00 and this is a no-op; + * if we are flag = 1, mask = 11...11 and this is identical to secp256k1_scalar_negate */ + uint32_t mask = !flag - 1; + uint32_t nonzero = 0xFFFFFFFFUL * (secp256k1_scalar_is_zero(r) == 0); + uint64_t t = (uint64_t)(r->d[0] ^ mask) + ((SECP256K1_N_0 + 1) & mask); + r->d[0] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[1] ^ mask) + (SECP256K1_N_1 & mask); + r->d[1] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[2] ^ mask) + (SECP256K1_N_2 & mask); + r->d[2] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[3] ^ mask) + (SECP256K1_N_3 & mask); + r->d[3] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[4] ^ mask) + (SECP256K1_N_4 & mask); + r->d[4] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[5] ^ mask) + (SECP256K1_N_5 & mask); + r->d[5] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[6] ^ mask) + (SECP256K1_N_6 & mask); + r->d[6] = t & nonzero; t >>= 32; + t += (uint64_t)(r->d[7] ^ mask) + (SECP256K1_N_7 & mask); + r->d[7] = t & nonzero; + return 2 * (mask == 0) - 1; +} + + +/* Inspired by the macros in OpenSSL's crypto/bn/asm/x86_64-gcc.c. */ + +/** Add a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd(a,b) { \ +uint32_t tl, th; \ +{ \ +uint64_t t = (uint64_t)a * b; \ +th = t >> 32; /* at most 0xFFFFFFFE */ \ +tl = t; \ +} \ +c0 += tl; /* overflow is handled on the next line */ \ +th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ +c1 += th; /* overflow is handled on the next line */ \ +c2 += (c1 < th) ? 1 : 0; /* never overflows by contract (verified in the next line) */ \ +VERIFY_CHECK((c1 >= th) || (c2 != 0)); \ +} + +/** Add a*b to the number defined by (c0,c1). c1 must never overflow. */ +#define muladd_fast(a,b) { \ +uint32_t tl, th; \ +{ \ +uint64_t t = (uint64_t)a * b; \ +th = t >> 32; /* at most 0xFFFFFFFE */ \ +tl = t; \ +} \ +c0 += tl; /* overflow is handled on the next line */ \ +th += (c0 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ +c1 += th; /* never overflows by contract (verified in the next line) */ \ +VERIFY_CHECK(c1 >= th); \ +} + +/** Add 2*a*b to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define muladd2(a,b) { \ +uint32_t tl, th, th2, tl2; \ +{ \ +uint64_t t = (uint64_t)a * b; \ +th = t >> 32; /* at most 0xFFFFFFFE */ \ +tl = t; \ +} \ +th2 = th + th; /* at most 0xFFFFFFFE (in case th was 0x7FFFFFFF) */ \ +c2 += (th2 < th) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((th2 >= th) || (c2 != 0)); \ +tl2 = tl + tl; /* at most 0xFFFFFFFE (in case the lowest 63 bits of tl were 0x7FFFFFFF) */ \ +th2 += (tl2 < tl) ? 1 : 0; /* at most 0xFFFFFFFF */ \ +c0 += tl2; /* overflow is handled on the next line */ \ +th2 += (c0 < tl2) ? 1 : 0; /* second overflow is handled on the next line */ \ +c2 += (c0 < tl2) & (th2 == 0); /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((c0 >= tl2) || (th2 != 0) || (c2 != 0)); \ +c1 += th2; /* overflow is handled on the next line */ \ +c2 += (c1 < th2) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((c1 >= th2) || (c2 != 0)); \ +} + +/** Add a to the number defined by (c0,c1,c2). c2 must never overflow. */ +#define sumadd(a) { \ +unsigned int over; \ +c0 += (a); /* overflow is handled on the next line */ \ +over = (c0 < (a)) ? 1 : 0; \ +c1 += over; /* overflow is handled on the next line */ \ +c2 += (c1 < over) ? 1 : 0; /* never overflows by contract */ \ +} + +/** Add a to the number defined by (c0,c1). c1 must never overflow, c2 must be zero. */ +#define sumadd_fast(a) { \ +c0 += (a); /* overflow is handled on the next line */ \ +c1 += (c0 < (a)) ? 1 : 0; /* never overflows by contract (verified the next line) */ \ +VERIFY_CHECK((c1 != 0) | (c0 >= (a))); \ +VERIFY_CHECK(c2 == 0); \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. */ +#define extract(n) { \ +(n) = c0; \ +c0 = c1; \ +c1 = c2; \ +c2 = 0; \ +} + +/** Extract the lowest 32 bits of (c0,c1,c2) into n, and left shift the number 32 bits. c2 is required to be zero. */ +#define extract_fast(n) { \ +(n) = c0; \ +c0 = c1; \ +c1 = 0; \ +VERIFY_CHECK(c2 == 0); \ +} + +static void secp256k1_scalar_reduce_512(secp256k1_scalar *r, const uint32_t *l) { + uint64_t c; + uint32_t n0 = l[8], n1 = l[9], n2 = l[10], n3 = l[11], n4 = l[12], n5 = l[13], n6 = l[14], n7 = l[15]; + uint32_t m0, m1, m2, m3, m4, m5, m6, m7, m8, m9, m10, m11, m12; + uint32_t p0, p1, p2, p3, p4, p5, p6, p7, p8; + + /* 96 bit accumulator. */ + uint32_t c0, c1, c2; + + /* Reduce 512 bits into 385. */ + /* m[0..12] = l[0..7] + n[0..7] * SECP256K1_N_C. */ + c0 = l[0]; c1 = 0; c2 = 0; + muladd_fast(n0, SECP256K1_N_C_0); + extract_fast(m0); + sumadd_fast(l[1]); + muladd(n1, SECP256K1_N_C_0); + muladd(n0, SECP256K1_N_C_1); + extract(m1); + sumadd(l[2]); + muladd(n2, SECP256K1_N_C_0); + muladd(n1, SECP256K1_N_C_1); + muladd(n0, SECP256K1_N_C_2); + extract(m2); + sumadd(l[3]); + muladd(n3, SECP256K1_N_C_0); + muladd(n2, SECP256K1_N_C_1); + muladd(n1, SECP256K1_N_C_2); + muladd(n0, SECP256K1_N_C_3); + extract(m3); + sumadd(l[4]); + muladd(n4, SECP256K1_N_C_0); + muladd(n3, SECP256K1_N_C_1); + muladd(n2, SECP256K1_N_C_2); + muladd(n1, SECP256K1_N_C_3); + sumadd(n0); + extract(m4); + sumadd(l[5]); + muladd(n5, SECP256K1_N_C_0); + muladd(n4, SECP256K1_N_C_1); + muladd(n3, SECP256K1_N_C_2); + muladd(n2, SECP256K1_N_C_3); + sumadd(n1); + extract(m5); + sumadd(l[6]); + muladd(n6, SECP256K1_N_C_0); + muladd(n5, SECP256K1_N_C_1); + muladd(n4, SECP256K1_N_C_2); + muladd(n3, SECP256K1_N_C_3); + sumadd(n2); + extract(m6); + sumadd(l[7]); + muladd(n7, SECP256K1_N_C_0); + muladd(n6, SECP256K1_N_C_1); + muladd(n5, SECP256K1_N_C_2); + muladd(n4, SECP256K1_N_C_3); + sumadd(n3); + extract(m7); + muladd(n7, SECP256K1_N_C_1); + muladd(n6, SECP256K1_N_C_2); + muladd(n5, SECP256K1_N_C_3); + sumadd(n4); + extract(m8); + muladd(n7, SECP256K1_N_C_2); + muladd(n6, SECP256K1_N_C_3); + sumadd(n5); + extract(m9); + muladd(n7, SECP256K1_N_C_3); + sumadd(n6); + extract(m10); + sumadd_fast(n7); + extract_fast(m11); + VERIFY_CHECK(c0 <= 1); + m12 = c0; + + /* Reduce 385 bits into 258. */ + /* p[0..8] = m[0..7] + m[8..12] * SECP256K1_N_C. */ + c0 = m0; c1 = 0; c2 = 0; + muladd_fast(m8, SECP256K1_N_C_0); + extract_fast(p0); + sumadd_fast(m1); + muladd(m9, SECP256K1_N_C_0); + muladd(m8, SECP256K1_N_C_1); + extract(p1); + sumadd(m2); + muladd(m10, SECP256K1_N_C_0); + muladd(m9, SECP256K1_N_C_1); + muladd(m8, SECP256K1_N_C_2); + extract(p2); + sumadd(m3); + muladd(m11, SECP256K1_N_C_0); + muladd(m10, SECP256K1_N_C_1); + muladd(m9, SECP256K1_N_C_2); + muladd(m8, SECP256K1_N_C_3); + extract(p3); + sumadd(m4); + muladd(m12, SECP256K1_N_C_0); + muladd(m11, SECP256K1_N_C_1); + muladd(m10, SECP256K1_N_C_2); + muladd(m9, SECP256K1_N_C_3); + sumadd(m8); + extract(p4); + sumadd(m5); + muladd(m12, SECP256K1_N_C_1); + muladd(m11, SECP256K1_N_C_2); + muladd(m10, SECP256K1_N_C_3); + sumadd(m9); + extract(p5); + sumadd(m6); + muladd(m12, SECP256K1_N_C_2); + muladd(m11, SECP256K1_N_C_3); + sumadd(m10); + extract(p6); + sumadd_fast(m7); + muladd_fast(m12, SECP256K1_N_C_3); + sumadd_fast(m11); + extract_fast(p7); + p8 = c0 + m12; + VERIFY_CHECK(p8 <= 2); + + /* Reduce 258 bits into 256. */ + /* r[0..7] = p[0..7] + p[8] * SECP256K1_N_C. */ + c = p0 + (uint64_t)SECP256K1_N_C_0 * p8; + r->d[0] = c & 0xFFFFFFFFUL; c >>= 32; + c += p1 + (uint64_t)SECP256K1_N_C_1 * p8; + r->d[1] = c & 0xFFFFFFFFUL; c >>= 32; + c += p2 + (uint64_t)SECP256K1_N_C_2 * p8; + r->d[2] = c & 0xFFFFFFFFUL; c >>= 32; + c += p3 + (uint64_t)SECP256K1_N_C_3 * p8; + r->d[3] = c & 0xFFFFFFFFUL; c >>= 32; + c += p4 + (uint64_t)p8; + r->d[4] = c & 0xFFFFFFFFUL; c >>= 32; + c += p5; + r->d[5] = c & 0xFFFFFFFFUL; c >>= 32; + c += p6; + r->d[6] = c & 0xFFFFFFFFUL; c >>= 32; + c += p7; + r->d[7] = c & 0xFFFFFFFFUL; c >>= 32; + + /* Final reduction of r. */ + secp256k1_scalar_reduce(r, c + secp256k1_scalar_check_overflow(r)); +} + +static void secp256k1_scalar_mul_512(uint32_t *l, const secp256k1_scalar *a, const secp256k1_scalar *b) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7] * b[0..7]. */ + muladd_fast(a->d[0], b->d[0]); + extract_fast(l[0]); + muladd(a->d[0], b->d[1]); + muladd(a->d[1], b->d[0]); + extract(l[1]); + muladd(a->d[0], b->d[2]); + muladd(a->d[1], b->d[1]); + muladd(a->d[2], b->d[0]); + extract(l[2]); + muladd(a->d[0], b->d[3]); + muladd(a->d[1], b->d[2]); + muladd(a->d[2], b->d[1]); + muladd(a->d[3], b->d[0]); + extract(l[3]); + muladd(a->d[0], b->d[4]); + muladd(a->d[1], b->d[3]); + muladd(a->d[2], b->d[2]); + muladd(a->d[3], b->d[1]); + muladd(a->d[4], b->d[0]); + extract(l[4]); + muladd(a->d[0], b->d[5]); + muladd(a->d[1], b->d[4]); + muladd(a->d[2], b->d[3]); + muladd(a->d[3], b->d[2]); + muladd(a->d[4], b->d[1]); + muladd(a->d[5], b->d[0]); + extract(l[5]); + muladd(a->d[0], b->d[6]); + muladd(a->d[1], b->d[5]); + muladd(a->d[2], b->d[4]); + muladd(a->d[3], b->d[3]); + muladd(a->d[4], b->d[2]); + muladd(a->d[5], b->d[1]); + muladd(a->d[6], b->d[0]); + extract(l[6]); + muladd(a->d[0], b->d[7]); + muladd(a->d[1], b->d[6]); + muladd(a->d[2], b->d[5]); + muladd(a->d[3], b->d[4]); + muladd(a->d[4], b->d[3]); + muladd(a->d[5], b->d[2]); + muladd(a->d[6], b->d[1]); + muladd(a->d[7], b->d[0]); + extract(l[7]); + muladd(a->d[1], b->d[7]); + muladd(a->d[2], b->d[6]); + muladd(a->d[3], b->d[5]); + muladd(a->d[4], b->d[4]); + muladd(a->d[5], b->d[3]); + muladd(a->d[6], b->d[2]); + muladd(a->d[7], b->d[1]); + extract(l[8]); + muladd(a->d[2], b->d[7]); + muladd(a->d[3], b->d[6]); + muladd(a->d[4], b->d[5]); + muladd(a->d[5], b->d[4]); + muladd(a->d[6], b->d[3]); + muladd(a->d[7], b->d[2]); + extract(l[9]); + muladd(a->d[3], b->d[7]); + muladd(a->d[4], b->d[6]); + muladd(a->d[5], b->d[5]); + muladd(a->d[6], b->d[4]); + muladd(a->d[7], b->d[3]); + extract(l[10]); + muladd(a->d[4], b->d[7]); + muladd(a->d[5], b->d[6]); + muladd(a->d[6], b->d[5]); + muladd(a->d[7], b->d[4]); + extract(l[11]); + muladd(a->d[5], b->d[7]); + muladd(a->d[6], b->d[6]); + muladd(a->d[7], b->d[5]); + extract(l[12]); + muladd(a->d[6], b->d[7]); + muladd(a->d[7], b->d[6]); + extract(l[13]); + muladd_fast(a->d[7], b->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +static void secp256k1_scalar_sqr_512(uint32_t *l, const secp256k1_scalar *a) { + /* 96 bit accumulator. */ + uint32_t c0 = 0, c1 = 0, c2 = 0; + + /* l[0..15] = a[0..7]^2. */ + muladd_fast(a->d[0], a->d[0]); + extract_fast(l[0]); + muladd2(a->d[0], a->d[1]); + extract(l[1]); + muladd2(a->d[0], a->d[2]); + muladd(a->d[1], a->d[1]); + extract(l[2]); + muladd2(a->d[0], a->d[3]); + muladd2(a->d[1], a->d[2]); + extract(l[3]); + muladd2(a->d[0], a->d[4]); + muladd2(a->d[1], a->d[3]); + muladd(a->d[2], a->d[2]); + extract(l[4]); + muladd2(a->d[0], a->d[5]); + muladd2(a->d[1], a->d[4]); + muladd2(a->d[2], a->d[3]); + extract(l[5]); + muladd2(a->d[0], a->d[6]); + muladd2(a->d[1], a->d[5]); + muladd2(a->d[2], a->d[4]); + muladd(a->d[3], a->d[3]); + extract(l[6]); + muladd2(a->d[0], a->d[7]); + muladd2(a->d[1], a->d[6]); + muladd2(a->d[2], a->d[5]); + muladd2(a->d[3], a->d[4]); + extract(l[7]); + muladd2(a->d[1], a->d[7]); + muladd2(a->d[2], a->d[6]); + muladd2(a->d[3], a->d[5]); + muladd(a->d[4], a->d[4]); + extract(l[8]); + muladd2(a->d[2], a->d[7]); + muladd2(a->d[3], a->d[6]); + muladd2(a->d[4], a->d[5]); + extract(l[9]); + muladd2(a->d[3], a->d[7]); + muladd2(a->d[4], a->d[6]); + muladd(a->d[5], a->d[5]); + extract(l[10]); + muladd2(a->d[4], a->d[7]); + muladd2(a->d[5], a->d[6]); + extract(l[11]); + muladd2(a->d[5], a->d[7]); + muladd(a->d[6], a->d[6]); + extract(l[12]); + muladd2(a->d[6], a->d[7]); + extract(l[13]); + muladd_fast(a->d[7], a->d[7]); + extract_fast(l[14]); + VERIFY_CHECK(c1 == 0); + l[15] = c0; +} + +#undef sumadd +#undef sumadd_fast +#undef muladd +#undef muladd_fast +#undef muladd2 +#undef extract +#undef extract_fast + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + uint32_t l[16]; + secp256k1_scalar_mul_512(l, a, b); + secp256k1_scalar_reduce_512(r, l); +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = r->d[0] & ((1 << n) - 1); + r->d[0] = (r->d[0] >> n) + (r->d[1] << (32 - n)); + r->d[1] = (r->d[1] >> n) + (r->d[2] << (32 - n)); + r->d[2] = (r->d[2] >> n) + (r->d[3] << (32 - n)); + r->d[3] = (r->d[3] >> n) + (r->d[4] << (32 - n)); + r->d[4] = (r->d[4] >> n) + (r->d[5] << (32 - n)); + r->d[5] = (r->d[5] >> n) + (r->d[6] << (32 - n)); + r->d[6] = (r->d[6] >> n) + (r->d[7] << (32 - n)); + r->d[7] = (r->d[7] >> n); + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + uint32_t l[16]; + secp256k1_scalar_sqr_512(l, a); + secp256k1_scalar_reduce_512(r, l); +} + +#ifdef USE_ENDOMORPHISM +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + r1->d[0] = a->d[0]; + r1->d[1] = a->d[1]; + r1->d[2] = a->d[2]; + r1->d[3] = a->d[3]; + r1->d[4] = 0; + r1->d[5] = 0; + r1->d[6] = 0; + r1->d[7] = 0; + r2->d[0] = a->d[4]; + r2->d[1] = a->d[5]; + r2->d[2] = a->d[6]; + r2->d[3] = a->d[7]; + r2->d[4] = 0; + r2->d[5] = 0; + r2->d[6] = 0; + r2->d[7] = 0; +} +#endif + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return ((a->d[0] ^ b->d[0]) | (a->d[1] ^ b->d[1]) | (a->d[2] ^ b->d[2]) | (a->d[3] ^ b->d[3]) | (a->d[4] ^ b->d[4]) | (a->d[5] ^ b->d[5]) | (a->d[6] ^ b->d[6]) | (a->d[7] ^ b->d[7])) == 0; +} + +SECP256K1_INLINE static void secp256k1_scalar_mul_shift_var(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b, unsigned int shift) { + uint32_t l[16]; + unsigned int shiftlimbs; + unsigned int shiftlow; + unsigned int shifthigh; + VERIFY_CHECK(shift >= 256); + secp256k1_scalar_mul_512(l, a, b); + shiftlimbs = shift >> 5; + shiftlow = shift & 0x1F; + shifthigh = 32 - shiftlow; + r->d[0] = shift < 512 ? (l[0 + shiftlimbs] >> shiftlow | (shift < 480 && shiftlow ? (l[1 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[1] = shift < 480 ? (l[1 + shiftlimbs] >> shiftlow | (shift < 448 && shiftlow ? (l[2 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[2] = shift < 448 ? (l[2 + shiftlimbs] >> shiftlow | (shift < 416 && shiftlow ? (l[3 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[3] = shift < 416 ? (l[3 + shiftlimbs] >> shiftlow | (shift < 384 && shiftlow ? (l[4 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[4] = shift < 384 ? (l[4 + shiftlimbs] >> shiftlow | (shift < 352 && shiftlow ? (l[5 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[5] = shift < 352 ? (l[5 + shiftlimbs] >> shiftlow | (shift < 320 && shiftlow ? (l[6 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[6] = shift < 320 ? (l[6 + shiftlimbs] >> shiftlow | (shift < 288 && shiftlow ? (l[7 + shiftlimbs] << shifthigh) : 0)) : 0; + r->d[7] = shift < 288 ? (l[7 + shiftlimbs] >> shiftlow) : 0; + secp256k1_scalar_cadd_bit(r, 0, (l[(shift - 1) >> 5] >> ((shift - 1) & 0x1f)) & 1); +} + +#define ROTL32(x,n) ((x) << (n) | (x) >> (32-(n))) +#define QUARTERROUND(a,b,c,d) \ +a += b; d = ROTL32(d ^ a, 16); \ +c += d; b = ROTL32(b ^ c, 12); \ +a += b; d = ROTL32(d ^ a, 8); \ +c += d; b = ROTL32(b ^ c, 7); + +#ifdef WORDS_BIGENDIAN +#define LE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define BE32(p) (p) +#else +#define BE32(p) ((((p) & 0xFF) << 24) | (((p) & 0xFF00) << 8) | (((p) & 0xFF0000) >> 8) | (((p) & 0xFF000000) >> 24)) +#define LE32(p) (p) +#endif + +void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t idx) { + size_t n; + size_t over_count = 0; + uint32_t seed32[8]; + uint32_t x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15; + int over1, over2; + + memcpy((void *) seed32, (const void *) seed, 32); + do { + x0 = 0x61707865; + x1 = 0x3320646e; + x2 = 0x79622d32; + x3 = 0x6b206574; + x4 = LE32(seed32[0]); + x5 = LE32(seed32[1]); + x6 = LE32(seed32[2]); + x7 = LE32(seed32[3]); + x8 = LE32(seed32[4]); + x9 = LE32(seed32[5]); + x10 = LE32(seed32[6]); + x11 = LE32(seed32[7]); + x12 = idx; + x13 = idx >> 32; + x14 = 0; + x15 = over_count; + + n = 10; + while (n--) { + QUARTERROUND(x0, x4, x8,x12) + QUARTERROUND(x1, x5, x9,x13) + QUARTERROUND(x2, x6,x10,x14) + QUARTERROUND(x3, x7,x11,x15) + QUARTERROUND(x0, x5,x10,x15) + QUARTERROUND(x1, x6,x11,x12) + QUARTERROUND(x2, x7, x8,x13) + QUARTERROUND(x3, x4, x9,x14) + } + + x0 += 0x61707865; + x1 += 0x3320646e; + x2 += 0x79622d32; + x3 += 0x6b206574; + x4 += LE32(seed32[0]); + x5 += LE32(seed32[1]); + x6 += LE32(seed32[2]); + x7 += LE32(seed32[3]); + x8 += LE32(seed32[4]); + x9 += LE32(seed32[5]); + x10 += LE32(seed32[6]); + x11 += LE32(seed32[7]); + x12 += idx; + x13 += idx >> 32; + x14 += 0; + x15 += over_count; + + r1->d[7] = LE32(x0); + r1->d[6] = LE32(x1); + r1->d[5] = LE32(x2); + r1->d[4] = LE32(x3); + r1->d[3] = LE32(x4); + r1->d[2] = LE32(x5); + r1->d[1] = LE32(x6); + r1->d[0] = LE32(x7); + r2->d[7] = LE32(x8); + r2->d[6] = LE32(x9); + r2->d[5] = LE32(x10); + r2->d[4] = LE32(x11); + r2->d[3] = LE32(x12); + r2->d[2] = LE32(x13); + r2->d[1] = LE32(x14); + r2->d[0] = LE32(x15); + + over1 = secp256k1_scalar_check_overflow(r1); + over2 = secp256k1_scalar_check_overflow(r2); + over_count++; + } while (over1 | over2); +} + +#undef ROTL32 +#undef QUARTERROUND +#undef BE32 +#undef LE32 + +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ +#endif + diff --git a/src/secp256k1/src/scalar_impl.h b/src/secp256k1/src/scalar_impl.h index fa790570f..d69a94880 100644 --- a/src/secp256k1/src/scalar_impl.h +++ b/src/secp256k1/src/scalar_impl.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2014 Pieter Wuille * * Distributed under the MIT software license, see the accompanying * @@ -331,3 +333,341 @@ static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar #endif #endif /* SECP256K1_SCALAR_IMPL_H */ + + +#else +/********************************************************************** + * Copyright (c) 2014 Pieter Wuille * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_IMPL_H +#define SECP256K1_SCALAR_IMPL_H + +#include "group.h" +#include "scalar.h" + +#if defined HAVE_CONFIG_H +#include "libsecp256k1-config.h" +#endif + +#if defined(EXHAUSTIVE_TEST_ORDER) +#include "scalar_low_impl.h" +#elif defined(USE_SCALAR_4X64) +#include "scalar_4x64_impl.h" +#elif defined(USE_SCALAR_8X32) +#include "scalar_8x32_impl.h" +#else +#error "Please select scalar implementation" +#endif + +#ifndef USE_NUM_NONE +static void secp256k1_scalar_get_num(secp256k1_num *r, const secp256k1_scalar *a) { + unsigned char c[32]; + secp256k1_scalar_get_b32(c, a); + secp256k1_num_set_bin(r, c, 32); +} + +/** secp256k1 curve order, see secp256k1_ecdsa_const_order_as_fe in ecdsa_impl.h */ +static void secp256k1_scalar_order_get_num(secp256k1_num *r) { +#if defined(EXHAUSTIVE_TEST_ORDER) + static const unsigned char order[32] = { + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,EXHAUSTIVE_TEST_ORDER + }; +#else + static const unsigned char order[32] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, + 0xBA,0xAE,0xDC,0xE6,0xAF,0x48,0xA0,0x3B, + 0xBF,0xD2,0x5E,0x8C,0xD0,0x36,0x41,0x41 + }; +#endif + secp256k1_num_set_bin(r, order, 32); +} +#endif + +static void secp256k1_scalar_inverse(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(EXHAUSTIVE_TEST_ORDER) + int i; + *r = 0; + for (i = 0; i < EXHAUSTIVE_TEST_ORDER; i++) + if ((i * *x) % EXHAUSTIVE_TEST_ORDER == 1) + *r = i; + /* If this VERIFY_CHECK triggers we were given a noninvertible scalar (and thus + * have a composite group order; fix it in exhaustive_tests.c). */ + VERIFY_CHECK(*r != 0); +} +#else +secp256k1_scalar *t; +int i; +/* First compute xN as x ^ (2^N - 1) for some values of N, + * and uM as x ^ M for some values of M. */ +secp256k1_scalar x2, x3, x6, x8, x14, x28, x56, x112, x126; +secp256k1_scalar u2, u5, u9, u11, u13; + +secp256k1_scalar_sqr(&u2, x); +secp256k1_scalar_mul(&x2, &u2, x); +secp256k1_scalar_mul(&u5, &u2, &x2); +secp256k1_scalar_mul(&x3, &u5, &u2); +secp256k1_scalar_mul(&u9, &x3, &u2); +secp256k1_scalar_mul(&u11, &u9, &u2); +secp256k1_scalar_mul(&u13, &u11, &u2); + +secp256k1_scalar_sqr(&x6, &u13); +secp256k1_scalar_sqr(&x6, &x6); +secp256k1_scalar_mul(&x6, &x6, &u11); + +secp256k1_scalar_sqr(&x8, &x6); +secp256k1_scalar_sqr(&x8, &x8); +secp256k1_scalar_mul(&x8, &x8, &x2); + +secp256k1_scalar_sqr(&x14, &x8); +for (i = 0; i < 5; i++) { + secp256k1_scalar_sqr(&x14, &x14); +} +secp256k1_scalar_mul(&x14, &x14, &x6); + +secp256k1_scalar_sqr(&x28, &x14); +for (i = 0; i < 13; i++) { + secp256k1_scalar_sqr(&x28, &x28); +} +secp256k1_scalar_mul(&x28, &x28, &x14); + +secp256k1_scalar_sqr(&x56, &x28); +for (i = 0; i < 27; i++) { + secp256k1_scalar_sqr(&x56, &x56); +} +secp256k1_scalar_mul(&x56, &x56, &x28); + +secp256k1_scalar_sqr(&x112, &x56); +for (i = 0; i < 55; i++) { + secp256k1_scalar_sqr(&x112, &x112); +} +secp256k1_scalar_mul(&x112, &x112, &x56); + +secp256k1_scalar_sqr(&x126, &x112); +for (i = 0; i < 13; i++) { + secp256k1_scalar_sqr(&x126, &x126); +} +secp256k1_scalar_mul(&x126, &x126, &x14); + +/* Then accumulate the final result (t starts at x126). */ +t = &x126; +for (i = 0; i < 3; i++) { + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u5); /* 101 */ +for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x3); /* 111 */ +for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u5); /* 101 */ +for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u11); /* 1011 */ +for (i = 0; i < 4; i++) { + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u11); /* 1011 */ +for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x3); /* 111 */ +for (i = 0; i < 5; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x3); /* 111 */ +for (i = 0; i < 6; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u13); /* 1101 */ +for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u5); /* 101 */ +for (i = 0; i < 3; i++) { + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x3); /* 111 */ +for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u9); /* 1001 */ +for (i = 0; i < 6; i++) { /* 000 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u5); /* 101 */ +for (i = 0; i < 10; i++) { /* 0000000 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x3); /* 111 */ +for (i = 0; i < 4; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x3); /* 111 */ +for (i = 0; i < 9; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x8); /* 11111111 */ +for (i = 0; i < 5; i++) { /* 0 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u9); /* 1001 */ +for (i = 0; i < 6; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u11); /* 1011 */ +for (i = 0; i < 4; i++) { + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u13); /* 1101 */ +for (i = 0; i < 5; i++) { + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &x2); /* 11 */ +for (i = 0; i < 6; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u13); /* 1101 */ +for (i = 0; i < 10; i++) { /* 000000 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u13); /* 1101 */ +for (i = 0; i < 4; i++) { + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, &u9); /* 1001 */ +for (i = 0; i < 6; i++) { /* 00000 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(t, t, x); /* 1 */ +for (i = 0; i < 8; i++) { /* 00 */ + secp256k1_scalar_sqr(t, t); +} +secp256k1_scalar_mul(r, t, &x6); /* 111111 */ +} + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(a->d[0] & 1); +} +#endif + +static void secp256k1_scalar_inverse_var(secp256k1_scalar *r, const secp256k1_scalar *x) { +#if defined(USE_SCALAR_INV_BUILTIN) + secp256k1_scalar_inverse(r, x); +#elif defined(USE_SCALAR_INV_NUM) + unsigned char b[32]; + secp256k1_num n, m; + secp256k1_scalar t = *x; + secp256k1_scalar_get_b32(b, &t); + secp256k1_num_set_bin(&n, b, 32); + secp256k1_scalar_order_get_num(&m); + secp256k1_num_mod_inverse(&n, &n, &m); + secp256k1_num_get_bin(b, 32, &n); + secp256k1_scalar_set_b32(r, b, NULL); + /* Verify that the inverse was computed correctly, without GMP code. */ + secp256k1_scalar_mul(&t, &t, r); + CHECK(secp256k1_scalar_is_one(&t)); +#else +#error "Please select scalar inverse implementation" +#endif +} + +#ifdef USE_ENDOMORPHISM +#if defined(EXHAUSTIVE_TEST_ORDER) +/** + * Find k1 and k2 given k, such that k1 + k2 * lambda == k mod n; unlike in the + * full case we don't bother making k1 and k2 be small, we just want them to be + * nontrivial to get full test coverage for the exhaustive tests. We therefore + * (arbitrarily) set k2 = k + 5 and k1 = k - k2 * lambda. + */ +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r2 = (*a + 5) % EXHAUSTIVE_TEST_ORDER; + *r1 = (*a + (EXHAUSTIVE_TEST_ORDER - *r2) * EXHAUSTIVE_TEST_LAMBDA) % EXHAUSTIVE_TEST_ORDER; +} +#else +/** + * The Secp256k1 curve has an endomorphism, where lambda * (x, y) = (beta * x, y), where + * lambda is {0x53,0x63,0xad,0x4c,0xc0,0x5c,0x30,0xe0,0xa5,0x26,0x1c,0x02,0x88,0x12,0x64,0x5a, + * 0x12,0x2e,0x22,0xea,0x20,0x81,0x66,0x78,0xdf,0x02,0x96,0x7c,0x1b,0x23,0xbd,0x72} + * + * "Guide to Elliptic Curve Cryptography" (Hankerson, Menezes, Vanstone) gives an algorithm + * (algorithm 3.74) to find k1 and k2 given k, such that k1 + k2 * lambda == k mod n, and k1 + * and k2 have a small size. + * It relies on constants a1, b1, a2, b2. These constants for the value of lambda above are: + * + * - a1 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * - b1 = -{0xe4,0x43,0x7e,0xd6,0x01,0x0e,0x88,0x28,0x6f,0x54,0x7f,0xa9,0x0a,0xbf,0xe4,0xc3} + * - a2 = {0x01,0x14,0xca,0x50,0xf7,0xa8,0xe2,0xf3,0xf6,0x57,0xc1,0x10,0x8d,0x9d,0x44,0xcf,0xd8} + * - b2 = {0x30,0x86,0xd2,0x21,0xa7,0xd4,0x6b,0xcd,0xe8,0x6c,0x90,0xe4,0x92,0x84,0xeb,0x15} + * + * The algorithm then computes c1 = round(b1 * k / n) and c2 = round(b2 * k / n), and gives + * k1 = k - (c1*a1 + c2*a2) and k2 = -(c1*b1 + c2*b2). Instead, we use modular arithmetic, and + * compute k1 as k - k2 * lambda, avoiding the need for constants a1 and a2. + * + * g1, g2 are precomputed constants used to replace division with a rounded multiplication + * when decomposing the scalar for an endomorphism-based point multiplication. + * + * The possibility of using precomputed estimates is mentioned in "Guide to Elliptic Curve + * Cryptography" (Hankerson, Menezes, Vanstone) in section 3.5. + * + * The derivation is described in the paper "Efficient Software Implementation of Public-Key + * Cryptography on Sensor Networks Using the MSP430X Microcontroller" (Gouvea, Oliveira, Lopez), + * Section 4.3 (here we use a somewhat higher-precision estimate): + * d = a1*b2 - b1*a2 + * g1 = round((2^272)*b2/d) + * g2 = round((2^272)*b1/d) + * + * (Note that 'd' is also equal to the curve order here because [a1,b1] and [a2,b2] are found + * as outputs of the Extended Euclidean Algorithm on inputs 'order' and 'lambda'). + * + * The function below splits a in r1 and r2, such that r1 + lambda * r2 == a (mod order). + */ + +static void secp256k1_scalar_split_lambda(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + secp256k1_scalar c1, c2; + static const secp256k1_scalar minus_lambda = SECP256K1_SCALAR_CONST( + 0xAC9C52B3UL, 0x3FA3CF1FUL, 0x5AD9E3FDUL, 0x77ED9BA4UL, + 0xA880B9FCUL, 0x8EC739C2UL, 0xE0CFC810UL, 0xB51283CFUL + ); + static const secp256k1_scalar minus_b1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00000000UL, + 0xE4437ED6UL, 0x010E8828UL, 0x6F547FA9UL, 0x0ABFE4C3UL + ); + static const secp256k1_scalar minus_b2 = SECP256K1_SCALAR_CONST( + 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFFUL, 0xFFFFFFFEUL, + 0x8A280AC5UL, 0x0774346DUL, 0xD765CDA8UL, 0x3DB1562CUL + ); + static const secp256k1_scalar g1 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x00003086UL, + 0xD221A7D4UL, 0x6BCDE86CUL, 0x90E49284UL, 0xEB153DABUL + ); + static const secp256k1_scalar g2 = SECP256K1_SCALAR_CONST( + 0x00000000UL, 0x00000000UL, 0x00000000UL, 0x0000E443UL, + 0x7ED6010EUL, 0x88286F54UL, 0x7FA90ABFUL, 0xE4C42212UL + ); + VERIFY_CHECK(r1 != a); + VERIFY_CHECK(r2 != a); + /* these _var calls are constant time since the shift amount is constant */ + secp256k1_scalar_mul_shift_var(&c1, a, &g1, 272); + secp256k1_scalar_mul_shift_var(&c2, a, &g2, 272); + secp256k1_scalar_mul(&c1, &c1, &minus_b1); + secp256k1_scalar_mul(&c2, &c2, &minus_b2); + secp256k1_scalar_add(r2, &c1, &c2); + secp256k1_scalar_mul(r1, r2, &minus_lambda); + secp256k1_scalar_add(r1, r1, a); +} +#endif +#endif + +#endif /* SECP256K1_SCALAR_IMPL_H */ +#endif + diff --git a/src/secp256k1/src/scalar_low.h b/src/secp256k1/src/scalar_low.h index 5836febc5..16b167f9c 100644 --- a/src/secp256k1/src/scalar_low.h +++ b/src/secp256k1/src/scalar_low.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2015 Andrew Poelstra * * Distributed under the MIT software license, see the accompanying * @@ -13,3 +15,21 @@ typedef uint32_t secp256k1_scalar; #endif /* SECP256K1_SCALAR_REPR_H */ + +#else +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_H +#define SECP256K1_SCALAR_REPR_H + +#include + +/** A scalar modulo the group order of the secp256k1 curve. */ +typedef uint32_t secp256k1_scalar; + +#endif /* SECP256K1_SCALAR_REPR_H */ +#endif diff --git a/src/secp256k1/src/scalar_low_impl.h b/src/secp256k1/src/scalar_low_impl.h index c80e70c5a..956cccd04 100644 --- a/src/secp256k1/src/scalar_low_impl.h +++ b/src/secp256k1/src/scalar_low_impl.h @@ -1,3 +1,5 @@ +#ifndef ENABLE_MODULE_MUSIG + /********************************************************************** * Copyright (c) 2015 Andrew Poelstra * * Distributed under the MIT software license, see the accompanying * @@ -112,3 +114,127 @@ SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const } #endif /* SECP256K1_SCALAR_REPR_IMPL_H */ + +#else +/********************************************************************** + * Copyright (c) 2015 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef SECP256K1_SCALAR_REPR_IMPL_H +#define SECP256K1_SCALAR_REPR_IMPL_H + +#include "scalar.h" + +#include + +SECP256K1_INLINE static int secp256k1_scalar_is_even(const secp256k1_scalar *a) { + return !(*a & 1); +} + +SECP256K1_INLINE static void secp256k1_scalar_clear(secp256k1_scalar *r) { *r = 0; } +SECP256K1_INLINE static void secp256k1_scalar_set_int(secp256k1_scalar *r, unsigned int v) { *r = v; } +SECP256K1_INLINE static void secp256k1_scalar_set_u64(secp256k1_scalar *r, uint64_t v) { *r = v % EXHAUSTIVE_TEST_ORDER; } + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + if (offset < 32) + return ((*a >> offset) & ((((uint32_t)1) << count) - 1)); + else + return 0; +} + +SECP256K1_INLINE static unsigned int secp256k1_scalar_get_bits_var(const secp256k1_scalar *a, unsigned int offset, unsigned int count) { + return secp256k1_scalar_get_bits(a, offset, count); +} + +SECP256K1_INLINE static int secp256k1_scalar_check_overflow(const secp256k1_scalar *a) { return *a >= EXHAUSTIVE_TEST_ORDER; } + +static int secp256k1_scalar_add(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a + *b) % EXHAUSTIVE_TEST_ORDER; + return *r < *b; +} + +static void secp256k1_scalar_cadd_bit(secp256k1_scalar *r, unsigned int bit, int flag) { + if (flag && bit < 32) + *r += (1 << bit); +#ifdef VERIFY + VERIFY_CHECK(secp256k1_scalar_check_overflow(r) == 0); +#endif +} + +static void secp256k1_scalar_set_b32(secp256k1_scalar *r, const unsigned char *b32, int *overflow) { + const int base = 0x100 % EXHAUSTIVE_TEST_ORDER; + int i; + *r = 0; + for (i = 0; i < 32; i++) { + *r = ((*r * base) + b32[i]) % EXHAUSTIVE_TEST_ORDER; + } + /* just deny overflow, it basically always happens */ + if (overflow) *overflow = 0; +} + +static void secp256k1_scalar_get_b32(unsigned char *bin, const secp256k1_scalar* a) { + memset(bin, 0, 32); + bin[28] = *a >> 24; bin[29] = *a >> 16; bin[30] = *a >> 8; bin[31] = *a; +} + +SECP256K1_INLINE static int secp256k1_scalar_is_zero(const secp256k1_scalar *a) { + return *a == 0; +} + +static void secp256k1_scalar_negate(secp256k1_scalar *r, const secp256k1_scalar *a) { + if (*a == 0) { + *r = 0; + } else { + *r = EXHAUSTIVE_TEST_ORDER - *a; + } +} + +SECP256K1_INLINE static int secp256k1_scalar_is_one(const secp256k1_scalar *a) { + return *a == 1; +} + +static int secp256k1_scalar_is_high(const secp256k1_scalar *a) { + return *a > EXHAUSTIVE_TEST_ORDER / 2; +} + +static int secp256k1_scalar_cond_negate(secp256k1_scalar *r, int flag) { + if (flag) secp256k1_scalar_negate(r, r); + return flag ? -1 : 1; +} + +static void secp256k1_scalar_mul(secp256k1_scalar *r, const secp256k1_scalar *a, const secp256k1_scalar *b) { + *r = (*a * *b) % EXHAUSTIVE_TEST_ORDER; +} + +static int secp256k1_scalar_shr_int(secp256k1_scalar *r, int n) { + int ret; + VERIFY_CHECK(n > 0); + VERIFY_CHECK(n < 16); + ret = *r & ((1 << n) - 1); + *r >>= n; + return ret; +} + +static void secp256k1_scalar_sqr(secp256k1_scalar *r, const secp256k1_scalar *a) { + *r = (*a * *a) % EXHAUSTIVE_TEST_ORDER; +} + +static void secp256k1_scalar_split_128(secp256k1_scalar *r1, secp256k1_scalar *r2, const secp256k1_scalar *a) { + *r1 = *a; + *r2 = 0; +} + +SECP256K1_INLINE static int secp256k1_scalar_eq(const secp256k1_scalar *a, const secp256k1_scalar *b) { + return *a == *b; +} + +void secp256k1_scalar_chacha20(secp256k1_scalar *r1, secp256k1_scalar *r2, const unsigned char *seed, uint64_t n) { + *r1 = (seed[0] + n) % EXHAUSTIVE_TEST_ORDER; + *r2 = (seed[1] + n) % EXHAUSTIVE_TEST_ORDER; +} + +#endif /* SECP256K1_SCALAR_REPR_IMPL_H */ +#endif + diff --git a/src/secp256k1/src/scratch.h b/src/secp256k1/src/scratch.h new file mode 100644 index 000000000..04faa9f0a --- /dev/null +++ b/src/secp256k1/src/scratch.h @@ -0,0 +1,40 @@ +/********************************************************************** + * Copyright (c) 2017 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCRATCH_ +#define _SECP256K1_SCRATCH_ + +#define SECP256K1_SCRATCH_MAX_FRAMES 5 + +/* The typedef is used internally; the struct name is used in the public API + * (where it is exposed as a different typedef) */ +typedef struct secp256k1_scratch_space_struct { + void *data[SECP256K1_SCRATCH_MAX_FRAMES]; + size_t offset[SECP256K1_SCRATCH_MAX_FRAMES]; + size_t frame_size[SECP256K1_SCRATCH_MAX_FRAMES]; + size_t frame; + size_t max_size; + const secp256k1_callback* error_callback; +} secp256k1_scratch; + +static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size); + +static void secp256k1_scratch_destroy(secp256k1_scratch* scratch); + +/** Attempts to allocate a new stack frame with `n` available bytes. Returns 1 on success, 0 on failure */ +static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects); + +/** Deallocates a stack frame */ +static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch); + +/** Returns the maximum allocation the scratch space will allow */ +static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t n_objects); + +/** Returns a pointer into the most recently allocated frame, or NULL if there is insufficient available space */ +static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t n); + +#endif + diff --git a/src/secp256k1/src/scratch_impl.h b/src/secp256k1/src/scratch_impl.h new file mode 100644 index 000000000..1ce3ff9b0 --- /dev/null +++ b/src/secp256k1/src/scratch_impl.h @@ -0,0 +1,87 @@ +/********************************************************************** + * Copyright (c) 2017 Andrew Poelstra * + * Distributed under the MIT software license, see the accompanying * + * file COPYING or http://www.opensource.org/licenses/mit-license.php.* + **********************************************************************/ + +#ifndef _SECP256K1_SCRATCH_IMPL_H_ +#define _SECP256K1_SCRATCH_IMPL_H_ + +#include "scratch.h" + +/* Using 16 bytes alignment because common architectures never have alignment + * requirements above 8 for any of the types we care about. In addition we + * leave some room because currently we don't care about a few bytes. + * TODO: Determine this at configure time. */ +#define ALIGNMENT 16 + +static secp256k1_scratch* secp256k1_scratch_create(const secp256k1_callback* error_callback, size_t max_size) { + secp256k1_scratch* ret = (secp256k1_scratch*)checked_malloc(error_callback, sizeof(*ret)); + if (ret != NULL) { + memset(ret, 0, sizeof(*ret)); + ret->max_size = max_size; + ret->error_callback = error_callback; + } + return ret; +} + +static void secp256k1_scratch_destroy(secp256k1_scratch* scratch) { + if (scratch != NULL) { + VERIFY_CHECK(scratch->frame == 0); + free(scratch); + } +} + +static size_t secp256k1_scratch_max_allocation(const secp256k1_scratch* scratch, size_t objects) { + size_t i = 0; + size_t allocated = 0; + for (i = 0; i < scratch->frame; i++) { + allocated += scratch->frame_size[i]; + } + if (scratch->max_size - allocated <= objects * ALIGNMENT) { + return 0; + } + return scratch->max_size - allocated - objects * ALIGNMENT; +} + +static int secp256k1_scratch_allocate_frame(secp256k1_scratch* scratch, size_t n, size_t objects) { + VERIFY_CHECK(scratch->frame < SECP256K1_SCRATCH_MAX_FRAMES); + + if (n <= secp256k1_scratch_max_allocation(scratch, objects)) { + n += objects * ALIGNMENT; + scratch->data[scratch->frame] = checked_malloc(scratch->error_callback, n); + if (scratch->data[scratch->frame] == NULL) { + return 0; + } + scratch->frame_size[scratch->frame] = n; + scratch->offset[scratch->frame] = 0; + scratch->frame++; + return 1; + } else { + return 0; + } +} + +static void secp256k1_scratch_deallocate_frame(secp256k1_scratch* scratch) { + VERIFY_CHECK(scratch->frame > 0); + scratch->frame -= 1; + free(scratch->data[scratch->frame]); +} + +static void *secp256k1_scratch_alloc(secp256k1_scratch* scratch, size_t size) { + void *ret; + size_t frame = scratch->frame - 1; + size = ((size + ALIGNMENT - 1) / ALIGNMENT) * ALIGNMENT; + + if (scratch->frame == 0 || size + scratch->offset[frame] > scratch->frame_size[frame]) { + return NULL; + } + ret = (void *) ((unsigned char *) scratch->data[frame] + scratch->offset[frame]); + memset(ret, 0, size); + scratch->offset[frame] += size; + + return ret; +} + +#endif + diff --git a/src/secp256k1/src/secp256k1.c b/src/secp256k1/src/secp256k1.c index cecb1550b..7861a5f79 100644 --- a/src/secp256k1/src/secp256k1.c +++ b/src/secp256k1/src/secp256k1.c @@ -4,7 +4,7 @@ * file COPYING or http://www.opensource.org/licenses/mit-license.php.* **********************************************************************/ -#include "include/secp256k1.h" +#include "../include/secp256k1.h" #include "util.h" #include "num_impl.h" @@ -17,6 +17,7 @@ #include "ecdsa_impl.h" #include "eckey_impl.h" #include "hash_impl.h" +#include "scratch_impl.h" #define ARG_CHECK(cond) do { \ if (EXPECT(!(cond), 0)) { \ @@ -114,7 +115,7 @@ void secp256k1_context_set_error_callback(secp256k1_context* ctx, void (*fun)(co ctx->error_callback.data = data; } -static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { +int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, const secp256k1_pubkey* pubkey) { if (sizeof(secp256k1_ge_storage) == 64) { /* When the secp256k1_ge_storage type is exactly 64 byte, use its * representation inside secp256k1_pubkey, as conversion is very fast. @@ -133,7 +134,7 @@ static int secp256k1_pubkey_load(const secp256k1_context* ctx, secp256k1_ge* ge, return 1; } -static void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { +void secp256k1_pubkey_save(secp256k1_pubkey* pubkey, secp256k1_ge* ge) { if (sizeof(secp256k1_ge_storage) == 64) { secp256k1_ge_storage s; secp256k1_ge_to_storage(&s, ge); @@ -339,6 +340,27 @@ static int nonce_function_rfc6979(unsigned char *nonce32, const unsigned char *m return 1; } +/* This nonce function is described in BIP-schnorr + * (https://github.com/sipa/bips/blob/bip-schnorr/bip-schnorr.mediawiki) */ +int secp256k1_nonce_function_bipschnorr(unsigned char *nonce32, const unsigned char *msg32, const unsigned char *key32, const unsigned char *algo16, void *data, unsigned int counter) { + secp256k1_sha256 sha; + (void) data; + (void) counter; + VERIFY_CHECK(counter == 0); + + /* Hash x||msg as per the spec */ + secp256k1_sha256_initialize(&sha); + secp256k1_sha256_write(&sha, key32, 32); + secp256k1_sha256_write(&sha, msg32, 32); + /* Hash in algorithm, which is not in the spec, but may be critical to + * users depending on it to avoid nonce reuse across algorithms. */ + if (algo16 != NULL) { + secp256k1_sha256_write(&sha, algo16, 16); + } + secp256k1_sha256_finalize(&sha, nonce32); + return 1; +} + const secp256k1_nonce_function secp256k1_nonce_function_rfc6979 = nonce_function_rfc6979; const secp256k1_nonce_function secp256k1_nonce_function_default = nonce_function_rfc6979; @@ -579,6 +601,9 @@ int secp256k1_ec_pubkey_combine(const secp256k1_context* ctx, secp256k1_pubkey * # include "modules/ecdh/main_impl.h" #endif +#include "../secp256k1/src/modules/schnorrsig/main_impl.h" +#include "../secp256k1/src/modules/musig/main_impl.h" + #ifdef ENABLE_MODULE_RECOVERY # include "modules/recovery/main_impl.h" #endif diff --git a/src/secp256k1/src/tests.c b/src/secp256k1/src/tests.c index f307b99d5..345fb0bdf 100644 --- a/src/secp256k1/src/tests.c +++ b/src/secp256k1/src/tests.c @@ -2405,7 +2405,7 @@ void ecmult_const_random_mult(void) { 0xb84e4e1b, 0xfb77e21f, 0x96baae2a, 0x63dec956 ); secp256k1_gej b; - secp256k1_ecmult_const(&b, &a, &xn); + secp256k1_ecmult_const(&b, &a, &xn,256); CHECK(secp256k1_ge_is_valid_var(&a)); ge_equals_gej(&expected_b, &b); @@ -2421,12 +2421,12 @@ void ecmult_const_commutativity(void) { random_scalar_order_test(&a); random_scalar_order_test(&b); - secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a); - secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b); + secp256k1_ecmult_const(&res1, &secp256k1_ge_const_g, &a,256); + secp256k1_ecmult_const(&res2, &secp256k1_ge_const_g, &b,256); secp256k1_ge_set_gej(&mid1, &res1); secp256k1_ge_set_gej(&mid2, &res2); - secp256k1_ecmult_const(&res1, &mid1, &b); - secp256k1_ecmult_const(&res2, &mid2, &a); + secp256k1_ecmult_const(&res1, &mid1, &b,256); + secp256k1_ecmult_const(&res2, &mid2, &a,256); secp256k1_ge_set_gej(&mid1, &res1); secp256k1_ge_set_gej(&mid2, &res2); ge_equals_ge(&mid1, &mid2); @@ -2442,13 +2442,13 @@ void ecmult_const_mult_zero_one(void) { secp256k1_scalar_negate(&negone, &one); random_group_element_test(&point); - secp256k1_ecmult_const(&res1, &point, &zero); + secp256k1_ecmult_const(&res1, &point, &zero,256); secp256k1_ge_set_gej(&res2, &res1); CHECK(secp256k1_ge_is_infinity(&res2)); - secp256k1_ecmult_const(&res1, &point, &one); + secp256k1_ecmult_const(&res1, &point, &one,256); secp256k1_ge_set_gej(&res2, &res1); ge_equals_ge(&res2, &point); - secp256k1_ecmult_const(&res1, &point, &negone); + secp256k1_ecmult_const(&res1, &point, &negone,256); secp256k1_gej_neg(&res1, &res1); secp256k1_ge_set_gej(&res2, &res1); ge_equals_ge(&res2, &point); @@ -2474,7 +2474,7 @@ void ecmult_const_chain_multiply(void) { for (i = 0; i < 100; ++i) { secp256k1_ge tmp; secp256k1_ge_set_gej(&tmp, &point); - secp256k1_ecmult_const(&point, &tmp, &scalar); + secp256k1_ecmult_const(&point, &tmp, &scalar,256); } secp256k1_ge_set_gej(&res, &point); ge_equals_gej(&res, &expected_point); diff --git a/src/secp256k1/src/tests_exhaustive.c b/src/secp256k1/src/tests_exhaustive.c index b040bb073..1e58c3b5f 100644 --- a/src/secp256k1/src/tests_exhaustive.c +++ b/src/secp256k1/src/tests_exhaustive.c @@ -174,7 +174,7 @@ void test_exhaustive_ecmult(const secp256k1_context *ctx, const secp256k1_ge *gr ge_equals_gej(&group[(i * r_log + j) % order], &tmp); if (i > 0) { - secp256k1_ecmult_const(&tmp, &group[i], &ng); + secp256k1_ecmult_const(&tmp, &group[i], &ng,256); ge_equals_gej(&group[(i * j) % order], &tmp); } } diff --git a/src/tui/LICENSE b/src/tui/LICENSE new file mode 100644 index 000000000..3300ef648 --- /dev/null +++ b/src/tui/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Anton Lysakov + +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 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/src/tui/README.md b/src/tui/README.md new file mode 100644 index 000000000..61e733794 --- /dev/null +++ b/src/tui/README.md @@ -0,0 +1,58 @@ +# Komodo Cryptoconditons Terminal User Interfaces (aka TUIs) + +These tools creating for demonstration and partial automation of Komodo cryptoconditions modules testing. (RogueCC game, AssetsCC, OraclesCC, GatewaysCC, MarmaraCC, ...) + + +Developer installation (on Ubuntu 18.04) : + +Python3 required for execution: + +* `sudo apt-get install python3.6 python3-pip libgnutls28-dev` + +pip packages needed: + +* `pip3 install setuptools wheel slick-bitcoinrpc` +* or `pip3 install -r requirements.txt` + +Starting: + +# TUI for RogueCC + +If you're looking for player 3 in 1 (daemon + game + TUI) multiOS bundle - please check `releases` of this repo. + +`python3 rogue_tui.py` + +![alt text](https://i.imgur.com/gkcxMGt.png) + +# TUI for OraclesCC + +Have files uploader/downloader functionality - also there is a AWS branch for AWS certificates uploading demonstration + +`python3 oracles_cc_tui.py` + +![alt text](https://i.imgur.com/tfHwRqc.png) + +# TUI for GatewaysCC + +![alt text](https://i.imgur.com/c8DPfpp.png) + +`python3 gateways_creation_tui.py` + +`python3 gateways_usage_tui.py` + +At the moment raw version of manual gateway how-to guide can be found here: https://docs.komodoplatform.com/cc/contracts/gateways/scenarios/tutorial.html I advice to read it before you start use this tool to understand the flow. + +# TUI for MarmaraCC + +`python3 marmara_tui.py` + +![alt text](https://i.imgur.com/uonMWHl.png) + +# TUI for AssetsCC (not much finished) + +`python3 assets_cc_tui.py` + +Before execution be sure than daemon for needed AC up. + + + diff --git a/src/tui/lib/logo.txt b/src/tui/lib/logo.txt new file mode 100644 index 000000000..15ace1ad5 --- /dev/null +++ b/src/tui/lib/logo.txt @@ -0,0 +1,39 @@ +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0xlc:ldOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0xo:,........';lxOXNMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMWNKkoc,..................':ox0XWMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMWNKkdc;............................,:ok0NWMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMWNKOdl;'.....................................,cdkKNWMMMMMMMMMMMMMM +MMMMMMMMMMMMMW0c'..............................................';kNMMMMMMMMMMMMM +MMMMMMMMMMMMMK:......................';:c:'......................,kWMMMMMMMMMMMM +MMMMMMMMMMMMXl...................;cdkKNWWNXOdl;'..................;OWMMMMMMMMMMM +MMMMMMMMMMMNo...............,cok0XWMMMMMMMMMMWNKkdc;'..............:KMMMMMMMMMMM +MMMMMMMMMMWx'...........;ox0XWMMMMMMMMMMMMMMMMMMMMWNKko:............lXMMMMMMMMMM +MMMMMMMMMWk,...........lXWMMMMMMMMMMMMWWWWMMMMMMMMMMMMMNx'...........oNMMMMMMMMM +MMMMMMMMW0;...........cKMMMMMMMMMWNXOdl::cdkKNWMMMMMMMMMNo...........'xWMMMMMMMM +MMMMMMMMKc...........;0WMMMMMWN0xl:,........';ldOXWMMMMMMXl...........,OWMMMMMMM +MMMMMMMXl...........,kWMMMMMMKl..................;OWMMMMMMK:...........;0MMMMMMM +MMMMMMNd...........'xNMMMMMMXl....................:0WMMMMMWO;...........cKMMMMMM +MMMMMNx'...........oNMMMMMMNd......................cKMMMMMMWk'...........lXMMMMM +MMMMWO,...........lXMMMMMMWx'.......................oXMMMMMMNd'...........dNMMMM +MMMMXc...........,OWMMMMMMK:........................,kWMMMMMMKc...........;0MMMM +MMMMWx'...........oXMMMMMMNd........................cKMMMMMMWk,...........lXMMMM +MMMMMNd...........'dNMMMMMMXl......................:0MMMMMMWO;...........cKMMMMM +MMMMMMXl...........,kWMMMMMMKc....................,OWMMMMMM0:...........;0MMMMMM +MMMMMMMKc...........;OWMMMMMW0:..................,kWMMMMMMXc...........,OWMMMMMM +MMMMMMMM0;...........:KMMMMMMWKko:,..........';lx0WMMMMMMNo...........'xWMMMMMMM +MMMMMMMMWk,...........lXMMMMMMMMWWXOxl:,,;cdOKNWMMMMMMMMNx'...........dNMMMMMMMM +MMMMMMMMMWx'...........dNMMMMMMMMMMMMWNXXNWMMMMMMMMMMMMWk,...........lXMMMMMMMMM +MMMMMMMMMMNo............cx0XWMMMMMMMMMMMMMMMMMMMMMMWN0kl,...........cKMMMMMMMMMM +MMMMMMMMMMMXl..............,:ok0XWMMMMMMMMMMMMWNKkdc;..............;0WMMMMMMMMMM +MMMMMMMMMMMMK:..................,cokKNWMMWNKOdl;'.................,kWMMMMMMMMMMM +MMMMMMMMMMMMWO;......................;cool;'.....................'xNMMMMMMMMMMMM +MMMMMMMMMMMMMWk;................................................'dNMMMMMMMMMMMMM +MMMMMMMMMMMMMMWXOxl;'.......................................,cdkKWMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMWNKOdc;..............................,cok0NWMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMWNKkoc,....................,:ox0XWMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0ko:,..........':lx0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0xl:,,;ldOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNNXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM +MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM \ No newline at end of file diff --git a/src/tui/lib/rpclib.py b/src/tui/lib/rpclib.py new file mode 100644 index 000000000..a79fc73d4 --- /dev/null +++ b/src/tui/lib/rpclib.py @@ -0,0 +1,129 @@ +import http +from slickrpc import Proxy + + +# RPC connection +def rpc_connect(rpc_user, rpc_password, port): + try: + rpc_connection = Proxy("http://%s:%s@127.0.0.1:%d"%(rpc_user, rpc_password, port)) + except Exception: + raise Exception("Connection error! Probably no daemon on selected port.") + return rpc_connection + + +# Non CC calls +def getinfo(rpc_connection): + try: + getinfo = rpc_connection.getinfo() + except Exception: + raise Exception("Connection error!") + return getinfo + + +def sendrawtransaction(rpc_connection, hex): + tx_id = rpc_connection.sendrawtransaction(hex) + return tx_id + + +def gettransaction(rpc_connection, tx_id): + transaction_info = rpc_connection.gettransaction(tx_id) + return transaction_info + + +def getrawtransaction(rpc_connection, tx_id): + rawtransaction = rpc_connection.getrawtransaction(tx_id) + return rawtransaction + + +def getbalance(rpc_connection): + balance = rpc_connection.getbalance() + return balance + +# Token CC calls +def token_create(rpc_connection, name, supply, description): + token_hex = rpc_connection.tokencreate(name, supply, description) + return token_hex + + +def token_info(rpc_connection, token_id): + token_info = rpc_connection.tokeninfo(token_id) + return token_info + + +#TODO: have to add option with pubkey input +def token_balance(rpc_connection, token_id): + token_balance = rpc_connection.tokenbalance(token_id) + return token_balance + +def token_list(rpc_connection): + token_list = rpc_connection.tokenlist() + return token_list + + +def token_convert(rpc_connection, evalcode, token_id, pubkey, supply): + token_convert_hex = rpc_connection.tokenconvert(evalcode, token_id, pubkey, supply) + return token_convert_hex + +def get_rawmempool(rpc_connection): + mempool = rpc_connection.getrawmempool() + return mempool + +# Oracle CC calls +def oracles_create(rpc_connection, name, description, data_type): + oracles_hex = rpc_connection.oraclescreate(name, description, data_type) + return oracles_hex + + +def oracles_register(rpc_connection, oracle_id, data_fee): + oracles_register_hex = rpc_connection.oraclesregister(oracle_id, data_fee) + return oracles_register_hex + + +def oracles_subscribe(rpc_connection, oracle_id, publisher_id, data_fee): + oracles_subscribe_hex = rpc_connection.oraclessubscribe(oracle_id, publisher_id, data_fee) + return oracles_subscribe_hex + + +def oracles_info(rpc_connection, oracle_id): + oracles_info = rpc_connection.oraclesinfo(oracle_id) + return oracles_info + + +def oracles_data(rpc_connection, oracle_id, hex_string): + oracles_data = rpc_connection.oraclesdata(oracle_id, hex_string) + return oracles_data + + +def oracles_list(rpc_connection): + oracles_list = rpc_connection.oracleslist() + return oracles_list + + +def oracles_samples(rpc_connection, oracletxid, batonutxo, num): + oracles_sample = rpc_connection.oraclessamples(oracletxid, batonutxo, num) + return oracles_sample + + +# Gateways CC calls +# Arguments changing dynamically depends of M N, so supposed to wrap it this way +# token_id, oracle_id, coin_name, token_supply, M, N + pubkeys for each N +def gateways_bind(rpc_connection, *args): + gateways_bind_hex = rpc_connection.gatewaysbind(*args) + return gateways_bind_hex + + +def gateways_deposit(rpc_connection, gateway_id, height, coin_name,\ + coin_txid, claim_vout, deposit_hex, proof, dest_pub, amount): + gateways_deposit_hex = rpc_connection.gatewaysdeposit(gateway_id, height, coin_name,\ + coin_txid, claim_vout, deposit_hex, proof, dest_pub, amount) + return gateways_deposit_hex + + +def gateways_claim(rpc_connection, gateway_id, coin_name, deposit_txid, dest_pub, amount): + gateways_claim_hex = rpc_connection.gatewaysclaim(gateway_id, coin_name, deposit_txid, dest_pub, amount) + return gateways_claim_hex + + +def gateways_withdraw(rpc_connection, gateway_id, coin_name, withdraw_pub, amount): + gateways_withdraw_hex = rpc_connection.gatewayswithdraw(gateway_id, coin_name, withdraw_pub, amount) + return gateways_withdraw_hex diff --git a/src/tui/lib/tuilib.py b/src/tui/lib/tuilib.py new file mode 100755 index 000000000..9a2fed639 --- /dev/null +++ b/src/tui/lib/tuilib.py @@ -0,0 +1,1974 @@ +from lib import rpclib +import json +import time +import re +import sys +import pickle +import platform +import os +import subprocess +import random +import signal +from slickrpc import Proxy +from binascii import hexlify +from binascii import unhexlify +from functools import partial +from shutil import copy + + +operating_system = platform.system() +if operating_system != 'Win64' and operating_system != 'Windows': + import readline + + +def colorize(string, color): + + colors = { + 'blue': '\033[94m', + 'magenta': '\033[95m', + 'green': '\033[92m', + 'red': '\033[91m' + } + if color not in colors: + return string + else: + return colors[color] + string + '\033[0m' + + +def rpc_connection_tui(): + # TODO: possible to save multiply entries from successfull sessions and ask user to choose then + while True: + restore_choice = input("Do you want to use connection details from previous session? [y/n]: ") + if restore_choice == "y": + try: + with open("connection.json", "r") as file: + connection_json = json.load(file) + rpc_user = connection_json["rpc_user"] + rpc_password = connection_json["rpc_password"] + rpc_port = connection_json["rpc_port"] + rpc_connection = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + except FileNotFoundError: + print(colorize("You do not have cached connection details. Please select n for connection setup", "red")) + break + elif restore_choice == "n": + rpc_user = input("Input your rpc user: ") + rpc_password = input("Input your rpc password: ") + rpc_port = input("Input your rpc port: ") + connection_details = {"rpc_user": rpc_user, + "rpc_password": rpc_password, + "rpc_port": rpc_port} + connection_json = json.dumps(connection_details) + with open("connection.json", "w+") as file: + file.write(connection_json) + rpc_connection = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + break + else: + print(colorize("Please input y or n", "red")) + return rpc_connection + + +def def_credentials(chain): + rpcport =''; + operating_system = platform.system() + if operating_system == 'Darwin': + ac_dir = os.environ['HOME'] + '/Library/Application Support/Komodo' + elif operating_system == 'Linux': + ac_dir = os.environ['HOME'] + '/.komodo' + elif operating_system == 'Win64' or operating_system == 'Windows': + ac_dir = '%s/komodo/' % os.environ['APPDATA'] + if chain == 'KMD': + coin_config_file = str(ac_dir + '/komodo.conf') + else: + coin_config_file = str(ac_dir + '/' + chain + '/' + chain + '.conf') + with open(coin_config_file, 'r') as f: + for line in f: + l = line.rstrip() + if re.search('rpcuser', l): + rpcuser = l.replace('rpcuser=', '') + elif re.search('rpcpassword', l): + rpcpassword = l.replace('rpcpassword=', '') + elif re.search('rpcport', l): + rpcport = l.replace('rpcport=', '') + if len(rpcport) == 0: + if chain == 'KMD': + rpcport = 7771 + else: + print("rpcport not in conf file, exiting") + print("check "+coin_config_file) + exit(1) + + return(Proxy("http://%s:%s@127.0.0.1:%d"%(rpcuser, rpcpassword, int(rpcport)))) + + +def getinfo_tui(rpc_connection): + + info_raw = rpclib.getinfo(rpc_connection) + if isinstance(info_raw, dict): + for key in info_raw: + print("{}: {}".format(key, info_raw[key])) + input("Press [Enter] to continue...") + else: + print("Error!\n") + print(info_raw) + input("\nPress [Enter] to continue...") + + +def token_create_tui(rpc_connection): + + while True: + try: + name = input("Set your token name: ") + supply = input("Set your token supply: ") + description = input("Set your token description: ") + except KeyboardInterrupt: + break + else: + token_hex = rpclib.token_create(rpc_connection, name, supply, description) + if token_hex['result'] == "error": + print(colorize("\nSomething went wrong!\n", "pink")) + print(token_hex) + print("\n") + input("Press [Enter] to continue...") + break + else: + try: + token_txid = rpclib.sendrawtransaction(rpc_connection, + token_hex['hex']) + except KeyError: + print(token_txid) + print("Error") + input("Press [Enter] to continue...") + break + finally: + print(colorize("Token creation transaction broadcasted: " + token_txid, "green")) + file = open("tokens_list", "a") + file.writelines(token_txid + "\n") + file.close() + print(colorize("Entry added to tokens_list file!\n", "green")) + input("Press [Enter] to continue...") + break + + +def oracle_create_tui(rpc_connection): + + print(colorize("\nAvailiable data types:\n", "blue")) + oracles_data_types = ["Ihh -> height, blockhash, merkleroot\ns -> <256 char string\nS -> <65536 char string\nd -> <256 binary data\nD -> <65536 binary data", + "c -> 1 byte signed little endian number, C unsigned\nt -> 2 byte signed little endian number, T unsigned", + "i -> 4 byte signed little endian number, I unsigned\nl -> 8 byte signed little endian number, L unsigned", + "h -> 32 byte hash\n"] + for oracles_type in oracles_data_types: + print(str(oracles_type)) + while True: + try: + name = input("Set your oracle name: ") + description = input("Set your oracle description: ") + oracle_data_type = input("Set your oracle type (e.g. Ihh): ") + except KeyboardInterrupt: + break + else: + oracle_hex = rpclib.oracles_create(rpc_connection, name, description, oracle_data_type) + if oracle_hex['result'] == "error": + print(colorize("\nSomething went wrong!\n", "pink")) + print(oracle_hex) + print("\n") + input("Press [Enter] to continue...") + break + else: + try: + oracle_txid = rpclib.sendrawtransaction(rpc_connection, oracle_hex['hex']) + except KeyError: + print(oracle_txid) + print("Error") + input("Press [Enter] to continue...") + break + finally: + print(colorize("Oracle creation transaction broadcasted: " + oracle_txid, "green")) + file = open("oracles_list", "a") + file.writelines(oracle_txid + "\n") + file.close() + print(colorize("Entry added to oracles_list file!\n", "green")) + input("Press [Enter] to continue...") + break + + +def oracle_register_tui(rpc_connection): + #TODO: have an idea since blackjoker new RPC call + #grab all list and printout only or which owner match with node pubkey + try: + print(colorize("Oracles created from this instance by TUI: \n", "blue")) + with open("oracles_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + while True: + try: + oracle_id = input("Input txid of oracle you want to register to: ") + data_fee = input("Set publisher datafee (in satoshis): ") + except KeyboardInterrupt: + break + oracle_register_hex = rpclib.oracles_register(rpc_connection, oracle_id, data_fee) + if oracle_register_hex['result'] == "error": + print(colorize("\nSomething went wrong!\n", "pink")) + print(oracle_register_hex) + print("\n") + input("Press [Enter] to continue...") + break + else: + try: + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex['hex']) + except KeyError: + print(oracle_register_hex) + print("Error") + input("Press [Enter] to continue...") + break + else: + print(colorize("Oracle registration transaction broadcasted: " + oracle_register_txid, "green")) + input("Press [Enter] to continue...") + break + + +def oracle_subscription_utxogen(rpc_connection): + # TODO: have an idea since blackjoker new RPC call + # grab all list and printout only or which owner match with node pubkey + try: + print(colorize("Oracles created from this instance by TUI: \n", "blue")) + with open("oracles_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + while True: + try: + oracle_id = input("Input oracle ID you want to subscribe to: ") + #printout to fast copypaste publisher id + oracle_info = rpclib.oracles_info(rpc_connection, oracle_id) + publishers = 0 + print(colorize("\nPublishers registered for a selected oracle: \n", "blue")) + try: + for entry in oracle_info["registered"]: + publisher = entry["publisher"] + print(publisher + "\n") + publishers = publishers + 1 + print("Total publishers:{}".format(publishers)) + except (KeyError, ConnectionResetError): + print(colorize("Please re-check your input. Oracle txid seems not valid.", "red")) + pass + print(colorize('_' * 65, "blue")) + print("\n") + if publishers == 0: + print(colorize("This oracle have no publishers to subscribe.\n" + "Please register as an oracle publisher first and/or wait since registration transaciton mined!", "red")) + input("Press [Enter] to continue...") + break + publisher_id = input("Input oracle publisher id you want to subscribe to: ") + data_fee = input("Input subscription fee (in COINS!): ") + utxo_num = int(input("Input how many transactions you want to broadcast: ")) + except KeyboardInterrupt: + break + while utxo_num > 0: + while True: + oracle_subscription_hex = rpclib.oracles_subscribe(rpc_connection, oracle_id, publisher_id, data_fee) + oracle_subscription_txid = rpclib.sendrawtransaction(rpc_connection, oracle_subscription_hex['hex']) + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscription_txid in mempool: + break + else: + pass + print(colorize("Oracle subscription transaction broadcasted: " + oracle_subscription_txid, "green")) + utxo_num = utxo_num - 1 + input("Press [Enter] to continue...") + break + +def gateways_bind_tui(rpc_connection): + # main loop with keyboard interrupt handling + while True: + try: + while True: + try: + print(colorize("Tokens created from this instance by TUI: \n", "blue")) + with open("tokens_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + token_id = input("Input id of token you want to use in gw bind: ") + try: + token_name = rpclib.token_info(rpc_connection, token_id)["name"] + except KeyError: + print(colorize("Not valid tokenid. Please try again.", "red")) + input("Press [Enter] to continue...") + token_info = rpclib.token_info(rpc_connection, token_id) + print(colorize("\n{} token total supply: {}\n".format(token_id, token_info["supply"]), "blue")) + token_supply = input("Input supply for token binding: ") + try: + print(colorize("\nOracles created from this instance by TUI: \n", "blue")) + with open("oracles_list", "r") as file: + for oracle in file: + print(oracle) + print(colorize('_' * 65, "blue")) + print("\n") + except FileNotFoundError: + print("Seems like a no oracles created from this instance yet\n") + pass + oracle_id = input("Input id of oracle you want to use in gw bind: ") + try: + oracle_name = rpclib.oracles_info(rpc_connection, oracle_id)["name"] + except KeyError: + print(colorize("Not valid oracleid. Please try again.", "red")) + input("Press [Enter] to continue...") + while True: + coin_name = input("Input external coin ticker (binded oracle and token need to have same name!): ") + if token_name == oracle_name and token_name == coin_name: + break + else: + print(colorize("Token name, oracle name and external coin ticker should match!", "red")) + while True: + M = input("Input minimal amount of pubkeys needed for transaction confirmation (1 for non-multisig gw): ") + N = input("Input maximal amount of pubkeys needed for transaction confirmation (1 for non-multisig gw): ") + if (int(N) >= int(M)): + break + else: + print("Maximal amount of pubkeys should be more or equal than minimal. Please try again.") + pubkeys = [] + for i in range(int(N)): + pubkeys.append(input("Input pubkey {}: ".format(i+1))) + pubtype = input("Input pubtype of external coin: ") + p2shtype = input("Input p2shtype of external coin: ") + wiftype = input("Input wiftype of external coin: ") + args = [rpc_connection, token_id, oracle_id, coin_name, token_supply, M, N] + new_args = [str(pubtype), str(p2shtype), wiftype] + args = args + pubkeys + new_args + # broadcasting block + try: + gateways_bind_hex = rpclib.gateways_bind(*args) + except Exception as e: + print(e) + input("Press [Enter] to continue...") + break + try: + gateways_bind_txid = rpclib.sendrawtransaction(rpc_connection, gateways_bind_hex["hex"]) + except Exception as e: + print(e) + print(gateways_bind_hex) + input("Press [Enter] to continue...") + break + else: + print(colorize("Gateway bind transaction broadcasted: " + gateways_bind_txid, "green")) + file = open("gateways_list", "a") + file.writelines(gateways_bind_txid + "\n") + file.close() + print(colorize("Entry added to gateways_list file!\n", "green")) + input("Press [Enter] to continue...") + break + break + except KeyboardInterrupt: + break + +# temporary :trollface: custom connection function solution +# to have connection to KMD daemon and cache it in separate file + + +def rpc_kmd_connection_tui(): + while True: + restore_choice = input("Do you want to use KMD daemon connection details from previous session? [y/n]: ") + if restore_choice == "y": + try: + with open("connection_kmd.json", "r") as file: + connection_json = json.load(file) + rpc_user = connection_json["rpc_user"] + rpc_password = connection_json["rpc_password"] + rpc_port = connection_json["rpc_port"] + rpc_connection_kmd = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + try: + print(rpc_connection_kmd.getinfo()) + print(colorize("Successfully connected!\n", "green")) + input("Press [Enter] to continue...") + break + except Exception as e: + print(e) + print(colorize("NOT CONNECTED!\n", "red")) + input("Press [Enter] to continue...") + break + except FileNotFoundError: + print(colorize("You do not have cached KMD daemon connection details." + " Please select n for connection setup", "red")) + input("Press [Enter] to continue...") + elif restore_choice == "n": + rpc_user = input("Input your rpc user: ") + rpc_password = input("Input your rpc password: ") + rpc_port = input("Input your rpc port: ") + connection_details = {"rpc_user": rpc_user, + "rpc_password": rpc_password, + "rpc_port": rpc_port} + connection_json = json.dumps(connection_details) + with open("connection_kmd.json", "w+") as file: + file.write(connection_json) + rpc_connection_kmd = rpclib.rpc_connect(rpc_user, rpc_password, int(rpc_port)) + try: + print(rpc_connection_kmd.getinfo()) + print(colorize("Successfully connected!\n", "green")) + input("Press [Enter] to continue...") + break + except Exception as e: + print(e) + print(colorize("NOT CONNECTED!\n", "red")) + input("Press [Enter] to continue...") + break + else: + print(colorize("Please input y or n", "red")) + return rpc_connection_kmd + + +def z_sendmany_twoaddresses(rpc_connection, sendaddress, recepient1, amount1, recepient2, amount2): + str_sending_block = "[{{\"address\":\"{}\",\"amount\":{}}},{{\"address\":\"{}\",\"amount\":{}}}]".format(recepient1, amount1, recepient2, amount2) + sending_block = json.loads(str_sending_block) + operation_id = rpc_connection.z_sendmany(sendaddress,sending_block) + return operation_id + + +def operationstatus_to_txid(rpc_connection, zstatus): + str_sending_block = "[\"{}\"]".format(zstatus) + sending_block = json.loads(str_sending_block) + operation_json = rpc_connection.z_getoperationstatus(sending_block) + operation_dump = json.dumps(operation_json) + operation_dict = json.loads(operation_dump)[0] + txid = operation_dict['result']['txid'] + return txid + + +def gateways_send_kmd(rpc_connection): + # TODO: have to handle CTRL+C on text input + print(colorize("Please be carefull when input wallet addresses and amounts since all transactions doing in real KMD!", "pink")) + print("Your addresses with balances: ") + list_address_groupings = rpc_connection.listaddressgroupings() + for address in list_address_groupings: + print(str(address) + "\n") + sendaddress = input("Input address from which you transfer KMD: ") + recepient1 = input("Input address which belongs to pubkey which will receive tokens: ") + amount1 = 0.0001 + recepient2 = input("Input gateway deposit address: ") + file = open("deposits_list", "a") + #have to show here deposit addresses for gateways created by user + amount2 = input("Input how many KMD you want to deposit on this gateway: ") + operation = z_sendmany_twoaddresses(rpc_connection, sendaddress, recepient1, amount1, recepient2, amount2) + print("Operation proceed! " + str(operation) + " Let's wait 2 seconds to get txid") + # trying to avoid pending status of operation + time.sleep(2) + txid = operationstatus_to_txid(rpc_connection, operation) + file.writelines(txid + "\n") + file.close() + print(colorize("KMD Transaction ID: " + str(txid) + " Entry added to deposits_list file", "green")) + input("Press [Enter] to continue...") + + +def gateways_deposit_tui(rpc_connection_assetchain, rpc_connection_komodo): + while True: + bind_txid = input("Input your gateway bind txid: ") + coin_name = input("Input your external coin ticker (e.g. KMD): ") + coin_txid = input("Input your deposit txid: ") + dest_pub = input("Input pubkey which claim deposit: ") + amount = input("Input amount of your deposit: ") + height = rpc_connection_komodo.getrawtransaction(coin_txid, 1)["height"] + deposit_hex = rpc_connection_komodo.getrawtransaction(coin_txid, 1)["hex"] + claim_vout = "0" + proof_sending_block = "[\"{}\"]".format(coin_txid) + proof = rpc_connection_komodo.gettxoutproof(json.loads(proof_sending_block)) + deposit_hex = rpclib.gateways_deposit(rpc_connection_assetchain, bind_txid, str(height), coin_name, \ + coin_txid, claim_vout, deposit_hex, proof, dest_pub, amount) + print(deposit_hex) + deposit_txid = rpclib.sendrawtransaction(rpc_connection_assetchain, deposit_hex["hex"]) + print("Done! Gateways deposit txid is: " + deposit_txid + " Please not forget to claim your deposit!") + input("Press [Enter] to continue...") + break + + +def gateways_claim_tui(rpc_connection): + while True: + bind_txid = input("Input your gateway bind txid: ") + coin_name = input("Input your external coin ticker (e.g. KMD): ") + deposit_txid = input("Input your gatewaysdeposit txid: ") + dest_pub = input("Input pubkey which claim deposit: ") + amount = input("Input amount of your deposit: ") + claim_hex = rpclib.gateways_claim(rpc_connection, bind_txid, coin_name, deposit_txid, dest_pub, amount) + try: + claim_txid = rpclib.sendrawtransaction(rpc_connection, claim_hex["hex"]) + except Exception as e: + print(e) + print(claim_hex) + input("Press [Enter] to continue...") + break + else: + print("Succesfully claimed! Claim transaction id: " + claim_txid) + input("Press [Enter] to continue...") + break + + +def gateways_withdrawal_tui(rpc_connection): + while True: + bind_txid = input("Input your gateway bind txid: ") + coin_name = input("Input your external coin ticker (e.g. KMD): ") + withdraw_pub = input("Input pubkey to which you want to withdraw: ") + amount = input("Input amount of withdrawal: ") + withdraw_hex = rpclib.gateways_withdraw(rpc_connection, bind_txid, coin_name, withdraw_pub, amount) + withdraw_txid = rpclib.sendrawtransaction(rpc_connection, withdraw_hex["hex"]) + print(withdraw_txid) + input("Press [Enter] to continue...") + break + + +def print_mempool(rpc_connection): + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + tx_counter = 0 + print(colorize("Transactions in mempool: \n", "magenta")) + for transaction in mempool: + print(transaction + "\n") + tx_counter = tx_counter + 1 + print("Total: " + str(tx_counter) + " transactions\n") + print("R + Enter to refresh list. E + Enter to exit menu." + "\n") + is_refresh = input("Choose your destiny: ") + if is_refresh == "R": + print("\n") + pass + elif is_refresh == "E": + print("\n") + break + else: + print("\nPlease choose R or E\n") + + +def print_tokens_list(rpc_connection): + # TODO: have to print it with tokeninfo to have sense + pass + + +def print_tokens_balances(rpc_connection): + # TODO: checking tokenbalance for each token from tokenlist and reflect non zero ones + pass + + +def hexdump(filename, chunk_size=1<<15): + data = "" + #add_spaces = partial(re.compile(b'(..)').sub, br'\1 ') + #write = getattr(sys.stdout, 'buffer', sys.stdout).write + with open(filename, 'rb') as file: + for chunk in iter(partial(file.read, chunk_size), b''): + data += str(hexlify(chunk).decode()) + return data + + +def convert_file_oracle_d(rpc_connection): + while True: + path = input("Input path to file you want to upload to oracle: ") + try: + hex_data = (hexdump(path, 1))[2:] + except Exception as e: + print(e) + print("Seems something goes wrong (I guess you've specified wrong path)!") + input("Press [Enter] to continue...") + break + else: + length = round(len(hex_data) / 2) + if length > 256: + print("Length: " + str(length) + " bytes") + print("File is too big for this app") + input("Press [Enter] to continue...") + break + else: + hex_length = format(length, '#04x')[2:] + data_for_oracle = str(hex_length) + hex_data + print("File hex representation: \n") + print(data_for_oracle + "\n") + print("Length: " + str(length) + " bytes") + print("File converted!") + new_oracle_hex = rpclib.oracles_create(rpc_connection, "tonyconvert", path, "d") + new_oracle_txid = rpclib.sendrawtransaction(rpc_connection, new_oracle_hex["hex"]) + time.sleep(0.5) + oracle_register_hex = rpclib.oracles_register(rpc_connection, new_oracle_txid, "10000") + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex["hex"]) + time.sleep(0.5) + oracle_subscribe_hex = rpclib.oracles_subscribe(rpc_connection, new_oracle_txid, rpclib.getinfo(rpc_connection)["pubkey"], "0.001") + oracle_subscribe_txid = rpclib.sendrawtransaction(rpc_connection, oracle_subscribe_hex["hex"]) + time.sleep(0.5) + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscribe_txid in mempool: + print("Waiting for oracle subscribtion tx to be mined" + "\n") + time.sleep(6) + pass + else: + break + oracles_data_hex = rpclib.oracles_data(rpc_connection, new_oracle_txid, data_for_oracle) + try: + oracle_data_txid = rpclib.sendrawtransaction(rpc_connection, oracles_data_hex["hex"]) + except Exception as e: + print(oracles_data_hex) + print(e) + print("Oracle created: " + str(new_oracle_txid)) + print("Data published: " + str(oracle_data_txid)) + input("Press [Enter] to continue...") + break + + +def convert_file_oracle_D(rpc_connection): + while True: + path = input("Input path to file you want to upload to oracle: ") + try: + hex_data = (hexdump(path, 1)) + except Exception as e: + print(e) + print("Seems something goes wrong (I guess you've specified wrong path)!") + input("Press [Enter] to continue...") + break + else: + length = round(len(hex_data) / 2) + # if length > 800000: + # print("Too big file size to upload for this version of program. Maximum size is 800KB.") + # input("Press [Enter] to continue...") + # break + if length > 8000: + # if file is more than 8000 bytes - slicing it to <= 8000 bytes chunks (16000 symbols = 8000 bytes) + data = [hex_data[i:i + 16000] for i in range(0, len(hex_data), 16000)] + chunks_amount = len(data) + # TODO: have to create oracle but subscribe this time chunks amount times to send whole file in same block + # TODO: 2 - on some point file will not fit block - have to find this point + # TODO: 3 way how I want to implement it first will keep whole file in RAM - have to implement some way to stream chunks to oracle before whole file readed + # TODO: have to "optimise" registration fee + # Maybe just check size first by something like a du ? + print("Length: " + str(length) + " bytes.\n Chunks amount: " + str(chunks_amount)) + new_oracle_hex = rpclib.oracles_create(rpc_connection, "tonyconvert_" + str(chunks_amount), path, "D") + new_oracle_txid = rpclib.sendrawtransaction(rpc_connection, new_oracle_hex["hex"]) + time.sleep(0.5) + oracle_register_hex = rpclib.oracles_register(rpc_connection, new_oracle_txid, "10000") + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex["hex"]) + # subscribe chunks_amount + 1 times, but lets limit our broadcasting 100 tx per block (800KB/block) + if chunks_amount > 100: + utxo_num = 101 + else: + utxo_num = chunks_amount + while utxo_num > 0: + while True: + oracle_subscription_hex = rpclib.oracles_subscribe(rpc_connection, new_oracle_txid, rpclib.getinfo(rpc_connection)["pubkey"], "0.001") + oracle_subscription_txid = rpclib.sendrawtransaction(rpc_connection, + oracle_subscription_hex['hex']) + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscription_txid in mempool: + break + else: + pass + print(colorize("Oracle subscription transaction broadcasted: " + oracle_subscription_txid, "green")) + utxo_num = utxo_num - 1 + # waiting for last broadcasted subscribtion transaction to be mined to be sure that money are on oracle balance + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscription_txid in mempool: + print("Waiting for oracle subscribtion tx to be mined" + "\n") + time.sleep(6) + pass + else: + break + print("Oracle preparation is finished. Oracle txid: " + new_oracle_txid) + # can publish data now + counter = 0 + for chunk in data: + hex_length_bigendian = format(round(len(chunk) / 2), '#06x')[2:] + # swap to get little endian length + a = hex_length_bigendian[2:] + b = hex_length_bigendian[:2] + hex_length = a + b + data_for_oracle = str(hex_length) + chunk + counter = counter + 1 + # print("Chunk number: " + str(counter) + "\n") + # print(data_for_oracle) + try: + oracles_data_hex = rpclib.oracles_data(rpc_connection, new_oracle_txid, data_for_oracle) + except Exception as e: + print(data_for_oracle) + print(e) + input("Press [Enter] to continue...") + break + # on broadcasting ensuring that previous one reached mempool before blast next one + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + oracle_data_txid = rpclib.sendrawtransaction(rpc_connection, oracles_data_hex["hex"]) + #time.sleep(0.1) + if oracle_data_txid in mempool: + break + else: + pass + # blasting not more than 100 at once (so maximum capacity per block can be changed here) + # but keep in mind that registration UTXOs amount needs to be changed too ! + if counter % 100 == 0 and chunks_amount > 100: + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_data_txid in mempool: + print("Waiting for previous data chunks to be mined before send new ones" + "\n") + print("Sent " + str(counter) + " chunks from " + str(chunks_amount)) + time.sleep(6) + pass + else: + break + + print("Last baton: " + oracle_data_txid) + input("Press [Enter] to continue...") + break + # if file suits single oraclesdata just broadcasting it straight without any slicing + else: + hex_length_bigendian = format(length, '#06x')[2:] + # swap to get little endian length + a = hex_length_bigendian[2:] + b = hex_length_bigendian[:2] + hex_length = a + b + data_for_oracle = str(hex_length) + hex_data + print("File hex representation: \n") + print(data_for_oracle + "\n") + print("Length: " + str(length) + " bytes") + print("File converted!") + new_oracle_hex = rpclib.oracles_create(rpc_connection, "tonyconvert_" + "1", path, "D") + new_oracle_txid = rpclib.sendrawtransaction(rpc_connection, new_oracle_hex["hex"]) + time.sleep(0.5) + oracle_register_hex = rpclib.oracles_register(rpc_connection, new_oracle_txid, "10000") + oracle_register_txid = rpclib.sendrawtransaction(rpc_connection, oracle_register_hex["hex"]) + time.sleep(0.5) + oracle_subscribe_hex = rpclib.oracles_subscribe(rpc_connection, new_oracle_txid, rpclib.getinfo(rpc_connection)["pubkey"], "0.001") + oracle_subscribe_txid = rpclib.sendrawtransaction(rpc_connection, oracle_subscribe_hex["hex"]) + time.sleep(0.5) + while True: + mempool = rpclib.get_rawmempool(rpc_connection) + if oracle_subscribe_txid in mempool: + print("Waiting for oracle subscribtion tx to be mined" + "\n") + time.sleep(6) + pass + else: + break + oracles_data_hex = rpclib.oracles_data(rpc_connection, new_oracle_txid, data_for_oracle) + try: + oracle_data_txid = rpclib.sendrawtransaction(rpc_connection, oracles_data_hex["hex"]) + except Exception as e: + print(oracles_data_hex) + print(e) + input("Press [Enter] to continue...") + break + else: + print("Oracle created: " + str(new_oracle_txid)) + print("Data published: " + str(oracle_data_txid)) + input("Press [Enter] to continue...") + break + + +def get_files_list(rpc_connection): + + start_time = time.time() + oracles_list = rpclib.oracles_list(rpc_connection) + files_list = [] + for oracle_txid in oracles_list: + oraclesinfo_result = rpclib.oracles_info(rpc_connection, oracle_txid) + description = oraclesinfo_result['description'] + name = oraclesinfo_result['name'] + if name[0:12] == 'tonyconvert_': + new_file = '[' + name + ': ' + description + ']: ' + oracle_txid + files_list.append(new_file) + print("--- %s seconds ---" % (time.time() - start_time)) + return files_list + + +def display_files_list(rpc_connection): + print("Scanning oracles. Please wait...") + list_to_display = get_files_list(rpc_connection) + while True: + for file in list_to_display: + print(file + "\n") + input("Press [Enter] to continue...") + break + + +def files_downloader(rpc_connection): + while True: + display_files_list(rpc_connection) + print("\n") + oracle_id = input("Input oracle ID you want to download file from: ") + output_path = input("Input output path for downloaded file (name included) e.g. /home/test.txt: ") + oracle_info = rpclib.oracles_info(rpc_connection, oracle_id) + name = oracle_info['name'] + latest_baton_txid = oracle_info['registered'][0]['batontxid'] + if name[0:12] == 'tonyconvert_': + # downloading process here + chunks_amount = int(name[12:]) + data = rpclib.oracles_samples(rpc_connection, oracle_id, latest_baton_txid, str(chunks_amount))["samples"] + for chunk in reversed(data): + with open(output_path, 'ab+') as file: + file.write(unhexlify(chunk[0])) + print("I hope that file saved to " + output_path + "\n") + input("Press [Enter] to continue...") + break + + else: + print("I cant recognize file inside this oracle. I'm very sorry, boss.") + input("Press [Enter] to continue...") + break + + +def marmara_receive_tui(rpc_connection): + while True: + issuer_pubkey = input("Input pubkey of person who do you want to receive MARMARA from: ") + issuance_sum = input("Input amount of MARMARA you want to receive: ") + blocks_valid = input("Input amount of blocks for cheque matures: ") + try: + marmara_receive_txinfo = rpc_connection.marmarareceive(issuer_pubkey, issuance_sum, "MARMARA", blocks_valid) + marmara_receive_txid = rpc_connection.sendrawtransaction(marmara_receive_txinfo["hex"]) + print("Marmara receive txid broadcasted: " + marmara_receive_txid + "\n") + print(json.dumps(marmara_receive_txinfo, indent=4, sort_keys=True) + "\n") + with open("receive_txids.txt", 'a+') as file: + file.write(marmara_receive_txid + "\n") + file.write(json.dumps(marmara_receive_txinfo, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to receive_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_receive_txinfo) + print(e) + print("Something went wrong. Please check your input") + + +def marmara_issue_tui(rpc_connection): + while True: + receiver_pubkey = input("Input pubkey of person who do you want to issue MARMARA: ") + issuance_sum = input("Input amount of MARMARA you want to issue: ") + maturing_block = input("Input number of block on which issuance mature: ") + approval_txid = input("Input receiving request transaction id: ") + try: + marmara_issue_txinfo = rpc_connection.marmaraissue(receiver_pubkey, issuance_sum, "MARMARA", maturing_block, approval_txid) + marmara_issue_txid = rpc_connection.sendrawtransaction(marmara_issue_txinfo["hex"]) + print("Marmara issuance txid broadcasted: " + marmara_issue_txid + "\n") + print(json.dumps(marmara_issue_txinfo, indent=4, sort_keys=True) + "\n") + with open("issue_txids.txt", "a+") as file: + file.write(marmara_issue_txid + "\n") + file.write(json.dumps(marmara_issue_txinfo, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to issue_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_issue_txinfo) + print(e) + print("Something went wrong. Please check your input") + + +def marmara_creditloop_tui(rpc_connection): + while True: + loop_txid = input("Input transaction ID of credit loop you want to get info about: ") + try: + marmara_creditloop_info = rpc_connection.marmaracreditloop(loop_txid) + print(json.dumps(marmara_creditloop_info, indent=4, sort_keys=True) + "\n") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_creditloop_info) + print(e) + print("Something went wrong. Please check your input") + + +def marmara_settlement_tui(rpc_connection): + while True: + loop_txid = input("Input transaction ID of credit loop to make settlement: ") + try: + marmara_settlement_info = rpc_connection.marmarasettlement(loop_txid) + marmara_settlement_txid = rpc_connection.sendrawtransaction(marmara_settlement_info["hex"]) + print("Loop " + loop_txid + " succesfully settled!\nSettlement txid: " + marmara_settlement_txid) + with open("settlement_txids.txt", "a+") as file: + file.write(marmara_settlement_txid + "\n") + file.write(json.dumps(marmara_settlement_info, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to settlement_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_settlement_info) + print(e) + print("Something went wrong. Please check your input") + input("Press [Enter] to continue...") + break + + +def marmara_lock_tui(rpc_connection): + while True: + amount = input("Input amount of coins you want to lock for settlement and staking: ") + unlock_height = input("Input height on which coins should be unlocked: ") + try: + marmara_lock_info = rpc_connection.marmaralock(amount, unlock_height) + marmara_lock_txid = rpc_connection.sendrawtransaction(marmara_lock_info["hex"]) + with open("lock_txids.txt", "a+") as file: + file.write(marmara_lock_txid + "\n") + file.write(json.dumps(marmara_lock_info, indent=4, sort_keys=True) + "\n") + print("Transaction id is saved to lock_txids.txt file.") + input("Press [Enter] to continue...") + break + except Exception as e: + print(e) + print("Something went wrong. Please check your input") + input("Press [Enter] to continue...") + break + + +def marmara_info_tui(rpc_connection): + while True: + firstheight = input("Input first height (default 0): ") + if not firstheight: + firstheight = "0" + lastheight = input("Input last height (default current (0) ): ") + if not lastheight: + lastheight = "0" + minamount = input("Input min amount (default 0): ") + if not minamount: + minamount = "0" + maxamount = input("Input max amount (default 0): ") + if not maxamount: + maxamount = "0" + issuerpk = input("Optional. Input issuer public key: ") + try: + if issuerpk: + marmara_info = rpc_connection.marmarainfo(firstheight, lastheight, minamount, maxamount, "MARMARA", issuerpk) + else: + marmara_info = rpc_connection.marmarainfo(firstheight, lastheight, minamount, maxamount) + print(json.dumps(marmara_info, indent=4, sort_keys=True) + "\n") + input("Press [Enter] to continue...") + break + except Exception as e: + print(marmara_info) + print(e) + print("Something went wrong. Please check your input") + input("Press [Enter] to continue...") + break + + +def rogue_game_info(rpc_connection, game_txid): + game_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + game_info = rpc_connection.cclib("gameinfo", "17", game_info_arg) + return game_info + + +def rogue_game_register(rpc_connection, game_txid, player_txid = False): + if player_txid: + registration_info_arg = '"' + "[%22" + game_txid + "%22,%22" + player_txid + "%22]" + '"' + else: + registration_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + registration_info = rpc_connection.cclib("register", "17", registration_info_arg) + return registration_info + + +def rogue_pending(rpc_connection): + rogue_pending_list = rpc_connection.cclib("pending", "17") + return rogue_pending_list + + +def rogue_bailout(rpc_connection, game_txid): + bailout_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + bailout_info = rpc_connection.cclib("bailout", "17", bailout_info_arg) + return bailout_info + + +def rogue_highlander(rpc_connection, game_txid): + highlander_info_arg = '"' + "[%22" + game_txid + "%22]" + '"' + highlander_info = rpc_connection.cclib("highlander", "17", highlander_info_arg) + return highlander_info + + +def rogue_players_list(rpc_connection): + rogue_players_list = rpc_connection.cclib("players", "17") + return rogue_players_list + + +def rogue_player_info(rpc_connection, playertxid): + player_info_arg = '"' + "[%22" + playertxid + "%22]" + '"' + player_info = rpc_connection.cclib("playerinfo", "17", player_info_arg) + return player_info + + +def rogue_extract(rpc_connection, game_txid, pubkey): + extract_info_arg = '"' + "[%22" + game_txid + "%22,%22" + pubkey + "%22]" + '"' + extract_info = rpc_connection.cclib("extract", "17", extract_info_arg) + return extract_info + + +def rogue_keystrokes(rpc_connection, game_txid, keystroke): + rogue_keystrokes_arg = '"' + "[%22" + game_txid + "%22,%22" + keystroke + "%22]" + '"' + keystroke_info = rpc_connection.cclib("keystrokes", "17", rogue_keystrokes_arg) + return keystroke_info + + +def print_multiplayer_games_list(rpc_connection): + while True: + pending_list = rogue_pending(rpc_connection) + multiplayer_pending_list = [] + for game in pending_list["pending"]: + if rogue_game_info(rpc_connection, game)["maxplayers"] > 1: + multiplayer_pending_list.append(game) + print("Multiplayer games availiable to join: \n") + for active_multiplayer_game in multiplayer_pending_list: + game_info = rogue_game_info(rpc_connection, active_multiplayer_game) + print(colorize("\n================================\n", "green")) + print("Game txid: " + game_info["gametxid"]) + print("Game buyin: " + str(game_info["buyin"])) + print("Game height: " + str(game_info["gameheight"])) + print("Start height: " + str(game_info["start"])) + print("Alive players: " + str(game_info["alive"])) + print("Registered players: " + str(game_info["numplayers"])) + print("Max players: " + str(game_info["maxplayers"])) + print(colorize("\n***\n", "blue")) + print("Players in game:") + for player in game_info["players"]: + print("Slot: " + str(player["slot"])) + if "baton" in player.keys(): + print("Baton: " + str(player["baton"])) + if "tokenid" in player.keys(): + print("Tokenid: " + str(player["tokenid"])) + print("Is mine?: " + str(player["ismine"])) + print(colorize("\nR + Enter - refresh list.\nE + Enter - to the game choice.\nCTRL + C - back to main menu", "blue")) + is_refresh = input("Choose your destiny: ") + if is_refresh == "R": + print("\n") + pass + elif is_refresh == "E": + print("\n") + break + else: + print("\nPlease choose R or E\n") + + +def rogue_newgame_singleplayer(rpc_connection, is_game_a_rogue=True): + try: + if is_game_a_rogue: + new_game_txid = rpc_connection.cclib("newgame", "17", "[1]")["txid"] + print("New singleplayer training game succesfully created. txid: " + new_game_txid) + while True: + mempool = rpc_connection.getrawmempool() + if new_game_txid in mempool: + print(colorize("Waiting for game transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Game transaction is mined", "green")) + break + else: + pending_games = rpc_connection.cclib("pending", "17")["pending"] + new_game_txid = random.choice(pending_games) + if is_game_a_rogue: + players_list = rogue_players_list(rpc_connection) + if len(players_list["playerdata"]) > 0: + print_players_list(rpc_connection) + while True: + is_choice_needed = input("Do you want to choose a player for this game? [y/n] ") + if is_choice_needed == "y": + player_txid = input("Please input player txid: ") + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid, player_txid)["txid"] + break + elif is_choice_needed == "n": + set_warriors_name(rpc_connection) + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] + break + else: + print("Please choose y or n !") + else: + print("No players available to select") + input("Press [Enter] to continue...") + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] + else: + newgame_regisration_txid = rogue_game_register(rpc_connection, new_game_txid)["txid"] + while True: + mempool = rpc_connection.getrawmempool() + if newgame_regisration_txid in mempool: + print(colorize("Waiting for registration transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Registration transaction is mined", "green")) + break + game_info = rogue_game_info(rpc_connection, new_game_txid) + start_time = time.time() + while True: + if is_game_a_rogue: + subprocess.call(["../cc/rogue/rogue", str(game_info["seed"]), str(game_info["gametxid"])]) + else: + subprocess.call(["../cc/games/tetris", str(game_info["seed"]), str(game_info["gametxid"])]) + time_elapsed = time.time() - start_time + if time_elapsed > 1: + break + else: + print("Game less than 1 second. Trying to start again") + time.sleep(1) + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout", "blue")) + time.sleep(5) + else: + break + #print("\nKeystrokes of this game:\n") + #time.sleep(0.5) + while True: + keystrokes_rpc_responses = find_game_keystrokes_in_log(new_game_txid)[1::2] + if len(keystrokes_rpc_responses) < 1: + print("No keystrokes broadcasted yet. Let's wait 5 seconds") + time.sleep(5) + else: + break + #print(keystrokes_rpc_responses) + for keystroke in keystrokes_rpc_responses: + json_keystroke = json.loads(keystroke)["result"] + if "status" in json_keystroke.keys() and json_keystroke["status"] == "error": + while True: + print("Trying to re-brodcast keystroke") + keystroke_rebroadcast = rogue_keystrokes(rpc_connection, json_keystroke["gametxid"], json_keystroke["keystrokes"]) + if "txid" in keystroke_rebroadcast.keys(): + print("Keystroke broadcasted! txid: " + keystroke_rebroadcast["txid"]) + break + else: + print("Let's try again in 5 seconds") + time.sleep(5) + # waiting for last keystroke confirmation here + last_keystroke_json = json.loads(keystrokes_rpc_responses[-1]) + while True: + while True: + try: + rpc_connection.sendrawtransaction(last_keystroke_json["result"]["hex"]) + except Exception as e: + pass + try: + confirmations_amount = rpc_connection.getrawtransaction(last_keystroke_json["result"]["txid"], 1)["confirmations"] + break + except Exception as e: + print(e) + print("Let's wait a little bit more") + time.sleep(5) + pass + if confirmations_amount < 1: + print("Last keystroke not confirmed yet! Let's wait a little") + time.sleep(10) + else: + print("Last keystroke confirmed!") + break + while True: + print("\nExtraction info:\n") + extraction_info = rogue_extract(rpc_connection, new_game_txid, rpc_connection.getinfo()["pubkey"]) + if extraction_info["status"] == "error": + print(colorize("Your warrior died or no any information about game was saved on blockchain", "red")) + print("If warrior was alive - try to wait a little (choose n to wait for a next block). If he is dead - you can bailout now (choose y).") + else: + print("Current game state:") + print("Game txid: " + extraction_info["gametxid"]) + print("Information about game saved on chain: " + extraction_info["extracted"]) + print("\n") + is_bailout_needed = input("Do you want to make bailout now [y] or wait for one more block [n]? [y/n]: ") + if is_bailout_needed == "y": + bailout_info = rogue_bailout(rpc_connection, new_game_txid) + if is_game_a_rogue: + while True: + try: + confirmations_amount = rpc_connection.getrawtransaction(bailout_info["txid"], 1)["confirmations"] + break + except Exception as e: + print(e) + print("Bailout not on blockchain yet. Let's wait a little bit more") + time.sleep(20) + pass + break + elif is_bailout_needed == "n": + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout", "blue")) + time.sleep(5) + else: + break + else: + print("Please choose y or n !") + print(bailout_info) + print("\nGame is finished!\n") + bailout_txid = bailout_info["txid"] + input("Press [Enter] to continue...") + except Exception as e: + print("Something went wrong.") + print(e) + input("Press [Enter] to continue...") + + +def play_multiplayer_game(rpc_connection): + # printing list of user active multiplayer games + active_games_list = rpc_connection.cclib("games", "17")["games"] + active_multiplayer_games_list = [] + for game in active_games_list: + gameinfo = rogue_game_info(rpc_connection, game) + if gameinfo["maxplayers"] > 1: + active_multiplayer_games_list.append(gameinfo) + games_counter = 0 + for active_multiplayer_game in active_multiplayer_games_list: + games_counter = games_counter + 1 + is_ready_to_start = False + try: + active_multiplayer_game["seed"] + is_ready_to_start = True + except Exception as e: + pass + print(colorize("\n================================\n", "green")) + print("Game txid: " + active_multiplayer_game["gametxid"]) + print("Game buyin: " + str(active_multiplayer_game["buyin"])) + if is_ready_to_start: + print(colorize("Ready for start!", "green")) + else: + print(colorize("Not ready for start yet, wait until start height!", "red")) + print("Game height: " + str(active_multiplayer_game["gameheight"])) + print("Start height: " + str(active_multiplayer_game["start"])) + print("Alive players: " + str(active_multiplayer_game["alive"])) + print("Registered players: " + str(active_multiplayer_game["numplayers"])) + print("Max players: " + str(active_multiplayer_game["maxplayers"])) + print(colorize("\n***\n", "blue")) + print("Players in game:") + for player in active_multiplayer_game["players"]: + print("Slot: " + str(player["slot"])) + print("Baton: " + str(player["baton"])) + print("Tokenid: " + str(player["tokenid"])) + print("Is mine?: " + str(player["ismine"])) + # asking user if he want to start any of them + while True: + start_game = input("\nDo you want to start any of your pendning multiplayer games?[y/n]: ") + if start_game == "y": + new_game_txid = input("Input txid of game which you want to start: ") + game_info = rogue_game_info(rpc_connection, new_game_txid) + try: + start_time = time.time() + while True: + subprocess.call(["cc/rogue/rogue", str(game_info["seed"]), str(game_info["gametxid"])]) + time_elapsed = time.time() - start_time + if time_elapsed > 1: + break + else: + print("Game less than 1 second. Trying to start again") + time.sleep(1) + except Exception as e: + print("Maybe game isn't ready for start yet or your input was not correct, sorry.") + input("Press [Enter] to continue...") + break + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout or highlander", "blue")) + time.sleep(5) + else: + break + while True: + keystrokes_rpc_responses = find_game_keystrokes_in_log(new_game_txid)[1::2] + if len(keystrokes_rpc_responses) < 1: + print("No keystrokes broadcasted yet. Let's wait 5 seconds") + time.sleep(5) + else: + break + for keystroke in keystrokes_rpc_responses: + json_keystroke = json.loads(keystroke)["result"] + if "status" in json_keystroke.keys() and json_keystroke["status"] == "error": + while True: + print("Trying to re-brodcast keystroke") + keystroke_rebroadcast = rogue_keystrokes(rpc_connection, json_keystroke["gametxid"], + json_keystroke["keystrokes"]) + if "txid" in keystroke_rebroadcast.keys(): + print("Keystroke broadcasted! txid: " + keystroke_rebroadcast["txid"]) + break + else: + print("Let's try again in 5 seconds") + time.sleep(5) + last_keystroke_json = json.loads(keystrokes_rpc_responses[-1]) + while True: + while True: + try: + confirmations_amount = rpc_connection.getrawtransaction(last_keystroke_json["result"]["txid"], 1)["confirmations"] + break + except Exception as e: + print(e) + print("Let's wait a little bit more") + rpc_connection.sendrawtransaction(last_keystroke_json["result"]["hex"]) + time.sleep(5) + pass + if confirmations_amount < 2: + print("Last keystroke not confirmed yet! Let's wait a little") + time.sleep(10) + else: + print("Last keystroke confirmed!") + break + while True: + print("\nExtraction info:\n") + extraction_info = rogue_extract(rpc_connection, new_game_txid, rpc_connection.getinfo()["pubkey"]) + if extraction_info["status"] == "error": + print(colorize("Your warrior died or no any information about game was saved on blockchain", "red")) + print("If warrior was alive - try to wait a little (choose n to wait for a next block). If he is dead - you can bailout now (choose y).") + else: + print("Current game state:") + print("Game txid: " + extraction_info["gametxid"]) + print("Information about game saved on chain: " + extraction_info["extracted"]) + print("\n") + is_bailout_needed = input( + "Do you want to make bailout now [y] or wait for one more block [n]? [y/n]: ") + if is_bailout_needed == "y": + if game_info["alive"] > 1: + bailout_info = rogue_bailout(rpc_connection, new_game_txid) + try: + bailout_txid = bailout_info["txid"] + print(bailout_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + except Exception: + highlander_info = rogue_highlander(rpc_connection, new_game_txid) + highlander_info = highlander_info["txid"] + print(highlander_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + else: + highlander_info = rogue_highlander(rpc_connection, new_game_txid) + if 'error' in highlander_info.keys() and highlander_info["error"] == 'numplayers != maxplayers': + bailout_info = rogue_bailout(rpc_connection, new_game_txid) + print(bailout_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + else: + print(highlander_info) + print("\nGame is finished!\n") + input("Press [Enter] to continue...") + break + elif is_bailout_needed == "n": + game_end_height = int(rpc_connection.getinfo()["blocks"]) + while True: + current_height = int(rpc_connection.getinfo()["blocks"]) + height_difference = current_height - game_end_height + if height_difference == 0: + print(current_height) + print(game_end_height) + print(colorize("Waiting for next block before bailout", "blue")) + time.sleep(5) + else: + break + break + break + if start_game == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def rogue_newgame_multiplayer(rpc_connection): + while True: + max_players = input("Input game max. players (>1): ") + if int(max_players) > 1: + break + else: + print("Please re-check your input") + input("Press [Enter] to continue...") + while True: + buyin = input("Input game buyin (>0.001): ") + if float(buyin) > 0.001: + break + else: + print("Please re-check your input") + input("Press [Enter] to continue...") + try: + new_game_txid = rpc_connection.cclib("newgame", "17", '"[' + max_players + "," + buyin + ']"')["txid"] + print(colorize("New multiplayer game succesfully created. txid: " + new_game_txid, "green")) + input("Press [Enter] to continue...") + except Exception as e: + print("Something went wrong.") + print(e) + input("Press [Enter] to continue...") + + +def rogue_join_multiplayer_game(rpc_connection): + while True: + try: + print_multiplayer_games_list(rpc_connection) + # TODO: optional player data txid (print players you have and ask if you want to choose one) + game_txid = input("Input txid of game you want to join: ") + try: + while True: + print_players_list(rpc_connection) + is_choice_needed = input("Do you want to choose a player for this game? [y/n] ") + if is_choice_needed == "y": + player_txid = input("Please input player txid: ") + newgame_regisration_txid = rogue_game_register(rpc_connection, game_txid, player_txid)["txid"] + break + elif is_choice_needed == "n": + set_warriors_name(rpc_connection) + newgame_regisration_txid = rogue_game_register(rpc_connection, game_txid)["txid"] + break + else: + print("Please choose y or n !") + except Exception as e: + print("Something went wrong. Maybe you're trying to register on game twice or don't have enough funds to pay buyin.") + print(e) + input("Press [Enter] to continue...") + break + print(colorize("Succesfully registered.", "green")) + while True: + mempool = rpc_connection.getrawmempool() + if newgame_regisration_txid in mempool: + print(colorize("Waiting for registration transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Registration transaction is mined", "green")) + break + print(newgame_regisration_txid) + input("Press [Enter] to continue...") + break + except KeyboardInterrupt: + break + + +def print_players_list(rpc_connection): + players_list = rogue_players_list(rpc_connection) + print(colorize("\nYou own " + str(players_list["numplayerdata"]) + " warriors\n", "blue")) + warrior_counter = 0 + for player in players_list["playerdata"]: + warrior_counter = warrior_counter + 1 + player_data = rogue_player_info(rpc_connection, player)["player"] + print(colorize("\n================================\n","green")) + print("Warrior " + str(warrior_counter)) + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n","blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + input("Press [Enter] to continue...") + + +def sell_warrior(rpc_connection): + print(colorize("Your brave warriors: \n", "blue")) + print_players_list(rpc_connection) + print("\n") + while True: + need_sell = input("Do you want to place order to sell any? [y/n]: ") + if need_sell == "y": + playertxid = input("Input playertxid of warrior you want to sell: ") + price = input("Input price (in ROGUE coins) you want to sell warrior for: ") + try: + tokenid = rogue_player_info(rpc_connection, playertxid)["player"]["tokenid"] + except Exception as e: + print(e) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + token_ask_raw = rpc_connection.tokenask("1", tokenid, price) + try: + token_ask_txid = rpc_connection.sendrawtransaction(token_ask_raw["hex"]) + except Exception as e: + print(e) + print(token_ask_raw) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Ask succesfully placed. Ask txid is: " + token_ask_txid, "green")) + input("Press [Enter] to continue...") + break + if need_sell == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +#TODO: have to combine into single scanner with different cases +def is_warrior_alive(rpc_connection, warrior_txid): + warrior_alive = False + raw_transaction = rpc_connection.getrawtransaction(warrior_txid, 1) + for vout in raw_transaction["vout"]: + if vout["value"] == 0.00000001 and rpc_connection.gettxout(raw_transaction["txid"], vout["n"]): + warrior_alive = True + return warrior_alive + + +def warriors_scanner(rpc_connection): + start_time = time.time() + token_list = rpc_connection.tokenlist() + my_warriors_list = rogue_players_list(rpc_connection) + warriors_list = {} + for token in token_list: + player_info = rogue_player_info(rpc_connection, token) + if "status" in player_info and player_info["status"] == "error": + pass + elif player_info["player"]["playertxid"] in my_warriors_list["playerdata"]: + pass + elif not is_warrior_alive(rpc_connection, player_info["player"]["playertxid"]): + pass + else: + warriors_list[token] = player_info["player"] + print("--- %s seconds ---" % (time.time() - start_time)) + return warriors_list + + +def warriors_scanner_for_rating(rpc_connection): + print("It can take some time") + token_list = rpc_connection.tokenlist() + my_warriors_list = rogue_players_list(rpc_connection) + actual_playerids = [] + warriors_list = {} + for token in token_list: + player_info = rogue_player_info(rpc_connection, token) + if "status" in player_info and player_info["status"] == "error": + pass + else: + while True: + if "batontxid" in player_info["player"].keys(): + player_info = rogue_player_info(rpc_connection, player_info["player"]["batontxid"]) + else: + actual_playerids.append(player_info["player"]["playertxid"]) + break + for player_id in actual_playerids: + player_info = rogue_player_info(rpc_connection, player_id) + if not is_warrior_alive(rpc_connection, player_info["player"]["playertxid"]): + pass + else: + warriors_list[player_id] = player_info["player"] + return warriors_list + + +def warriors_scanner_for_dex(rpc_connection): + start_time = time.time() + token_list = rpc_connection.tokenlist() + my_warriors_list = rogue_players_list(rpc_connection) + warriors_list = {} + for token in token_list: + player_info = rogue_player_info(rpc_connection, token) + if "status" in player_info and player_info["status"] == "error": + pass + elif player_info["player"]["tokenid"] in my_warriors_list["playerdata"]: + pass + else: + warriors_list[token] = player_info["player"] + print("--- %s seconds ---" % (time.time() - start_time)) + return warriors_list + + +def print_warrior_list(rpc_connection): + players_list = warriors_scanner(rpc_connection) + print(colorize("All warriors on ROGUE chain: \n", "blue")) + warrior_counter = 0 + for player in players_list: + warrior_counter = warrior_counter + 1 + player_data = rogue_player_info(rpc_connection, player)["player"] + print(colorize("\n================================\n","green")) + print("Warrior " + str(warrior_counter)) + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n","blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + input("Press [Enter] to continue...") + + +def place_bid_on_warriror(rpc_connection): + warriors_list = print_warrior_list(rpc_connection) + # TODO: have to drop my warriors or at least print my warriors ids + while True: + need_buy = input("Do you want to place order to buy some warrior? [y/n]: ") + if need_buy == "y": + playertxid = input("Input playertxid of warrior you want to place bid for: ") + price = input("Input price (in ROGUE coins) you want to buy warrior for: ") + tokenid = rogue_player_info(rpc_connection, playertxid)["player"]["tokenid"] + token_bid_raw = rpc_connection.tokenbid("1", tokenid, price) + try: + token_bid_txid = rpc_connection.sendrawtransaction(token_bid_raw["hex"]) + except Exception as e: + print(e) + print(token_bid_raw) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Bid succesfully placed. Bid txid is: " + token_bid_txid, "green")) + input("Press [Enter] to continue...") + break + if need_buy == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def check_incoming_bids(rpc_connection): + # TODO: have to scan for warriors which are in asks as well + players_list = rogue_players_list(rpc_connection) + incoming_orders = [] + for player in players_list["playerdata"]: + token_id = rogue_player_info(rpc_connection, player)["player"]["tokenid"] + orders = rpc_connection.tokenorders(token_id) + if len(orders) > 0: + for order in orders: + if order["funcid"] == "b": + incoming_orders.append(order) + return incoming_orders + + +def print_icoming_bids(rpc_connection): + incoming_bids = check_incoming_bids(rpc_connection) + for bid in incoming_bids: + print("Recieved bid for warrior " + bid["tokenid"]) + player_data = rogue_player_info(rpc_connection, bid["tokenid"])["player"] + print(colorize("\n================================\n", "green")) + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("\n================================\n", "blue")) + print("Order info: \n") + print("Bid txid: " + bid["txid"]) + print("Price: " + str(bid["price"]) + "\n") + if len(incoming_bids) == 0: + print(colorize("There is no any incoming orders!", "blue")) + input("Press [Enter] to continue...") + else: + while True: + want_to_sell = input("Do you want to fill any incoming bid? [y/n]: ") + if want_to_sell == "y": + bid_txid = input("Input bid txid you want to fill: ") + for bid in incoming_bids: + if bid_txid == bid["txid"]: + tokenid = bid["tokenid"] + fill_sum = bid["totalrequired"] + fillbid_hex = rpc_connection.tokenfillbid(tokenid, bid_txid, str(fill_sum)) + try: + fillbid_txid = rpc_connection.sendrawtransaction(fillbid_hex["hex"]) + except Exception as e: + print(e) + print(fillbid_hex) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Warrior succesfully sold. Txid is: " + fillbid_txid, "green")) + input("Press [Enter] to continue...") + break + if want_to_sell == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def find_warriors_asks(rpc_connection): + warriors_list = warriors_scanner_for_dex(rpc_connection) + warriors_asks = [] + for player in warriors_list: + orders = rpc_connection.tokenorders(player) + if len(orders) > 0: + for order in orders: + if order["funcid"] == "s": + warriors_asks.append(order) + for ask in warriors_asks: + print(colorize("\n================================\n", "green")) + print("Warrior selling on marketplace: " + ask["tokenid"]) + player_data = rogue_player_info(rpc_connection, ask["tokenid"])["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("Order info: \n", "red")) + print("Ask txid: " + ask["txid"]) + print("Price: " + str(ask["price"]) + "\n") + while True: + want_to_buy = input("Do you want to buy any warrior? [y/n]: ") + if want_to_buy == "y": + ask_txid = input("Input asktxid which you want to fill: ") + for ask in warriors_asks: + if ask_txid == ask["txid"]: + tokenid = ask["tokenid"] + try: + fillask_raw = rpc_connection.tokenfillask(tokenid, ask_txid, "1") + except Exception as e: + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + try: + fillask_txid = rpc_connection.sendrawtransaction(fillask_raw["hex"]) + except Exception as e: + print(e) + print(fillask_raw) + print("Something went wrong. Be careful with input next time.") + input("Press [Enter] to continue...") + break + print(colorize("Warrior succesfully bought. Txid is: " + fillask_txid, "green")) + input("Press [Enter] to continue...") + break + if want_to_buy == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def warriors_orders_check(rpc_connection): + my_orders_list = rpc_connection.mytokenorders("17") + warriors_orders = {} + for order in my_orders_list: + player_info = rogue_player_info(rpc_connection, order["tokenid"]) + if "status" in player_info and player_info["status"] == "error": + pass + else: + warriors_orders[order["tokenid"]] = order + bids_list = [] + asks_list = [] + for order in warriors_orders: + if warriors_orders[order]["funcid"] == "s": + asks_list.append(warriors_orders[order]) + else: + bids_list.append(order) + print(colorize("\nYour asks:\n", "blue")) + print(colorize("\n********************************\n", "red")) + for ask in asks_list: + print("txid: " + ask["txid"]) + print("Price: " + ask["price"]) + print("Warrior tokenid: " + ask["tokenid"]) + print(colorize("\n================================\n", "green")) + print("Warrior selling on marketplace: " + ask["tokenid"]) + player_data = rogue_player_info(rpc_connection, ask["tokenid"])["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("\n================================\n", "green")) + print(colorize("\nYour bids:\n", "blue")) + print(colorize("\n********************************\n", "red")) + for bid in bids_list: + print("txid: " + bid["txid"]) + print("Price: " + bid["price"]) + print("Warrior tokenid: " + bid["tokenid"]) + print(colorize("\n================================\n", "green")) + print("Warrior selling on marketplace: " + bid["tokenid"]) + player_data = rogue_player_info(rpc_connection, bid["tokenid"])["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print(colorize("\nInventory:\n", "blue")) + for item in player_data["pack"]: + print(item) + print("\nTotal packsize: " + str(player_data["packsize"]) + "\n") + print(colorize("\n================================\n", "green")) + while True: + need_order_change = input("Do you want to cancel any of your orders? [y/n]: ") + if need_order_change == "y": + while True: + ask_or_bid = input("Do you want cancel ask or bid? [a/b]: ") + if ask_or_bid == "a": + ask_txid = input("Input txid of ask you want to cancel: ") + warrior_tokenid = input("Input warrior token id for this ask: ") + try: + ask_cancellation_hex = rpc_connection.tokencancelask(warrior_tokenid, ask_txid) + ask_cancellation_txid = rpc_connection.sendrawtransaction(ask_cancellation_hex["hex"]) + except Exception as e: + print(colorize("Please re-check your input!", "red")) + print(colorize("Ask succefully cancelled. Cancellation txid: " + ask_cancellation_txid, "green")) + break + if ask_or_bid == "b": + bid_txid = input("Input txid of bid you want to cancel: ") + warrior_tokenid = input("Input warrior token id for this bid: ") + try: + bid_cancellation_hex = rpc_connection.tokencancelbid(warrior_tokenid, bid_txid) + bid_cancellation_txid = rpc_connection.sendrawtransaction(bid_cancellation_hex["hex"]) + except Exception as e: + print(colorize("Please re-check your input!", "red")) + print(colorize("Bid succefully cancelled. Cancellation txid: " + bid_cancellation_txid, "green")) + break + else: + print(colorize("Choose a or b!", "red")) + input("Press [Enter] to continue...") + break + if need_order_change == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def set_warriors_name(rpc_connection): + warriors_name = input("What warrior name do you want for legends and tales about your brave adventures?: ") + warrior_name_arg = '"' + "[%22" + warriors_name + "%22]" + '"' + set_name_status = rpc_connection.cclib("setname", "17", warrior_name_arg) + print(colorize("Warrior name succesfully set", "green")) + print("Result: " + set_name_status["result"]) + print("Name: " + set_name_status["pname"]) + input("Press [Enter] to continue...") + + +def top_warriors_rating(rpc_connection): + start_time = time.time() + warriors_list = warriors_scanner_for_rating(rpc_connection) + warriors_exp = {} + for warrior in warriors_list: + warriors_exp[warrior] = warriors_list[warrior]["experience"] + warriors_exp_sorted = {} + temp = [(k, warriors_exp[k]) for k in sorted(warriors_exp, key=warriors_exp.get, reverse=True)] + for k,v in temp: + warriors_exp_sorted[k] = v + counter = 0 + for experienced_warrior in warriors_exp_sorted: + if counter < 20: + counter = counter + 1 + print("\n" + str(counter) + " place.") + print(colorize("\n================================\n", "blue")) + player_data = rogue_player_info(rpc_connection, experienced_warrior)["player"] + print("Name: " + player_data["pname"] + "\n") + print("Player txid: " + player_data["playertxid"]) + print("Token txid: " + player_data["tokenid"]) + print("Hitpoints: " + str(player_data["hitpoints"])) + print("Strength: " + str(player_data["strength"])) + print("Level: " + str(player_data["level"])) + print("Experience: " + str(player_data["experience"])) + print("Dungeon Level: " + str(player_data["dungeonlevel"])) + print("Chain: " + player_data["chain"]) + print("--- %s seconds ---" % (time.time() - start_time)) + input("Press [Enter] to continue...") + + +def exit(): + sys.exit() + + +def warrior_trasnfer(rpc_connection): + print(colorize("Your brave warriors: \n", "blue")) + print_players_list(rpc_connection) + print("\n") + while True: + need_transfer = input("Do you want to transfer any warrior? [y/n]: ") + if need_transfer == "y": + warrior_tokenid = input("Input warrior tokenid: ") + recepient_pubkey = input("Input recepient pubkey: ") + try: + token_transfer_hex = rpc_connection.tokentransfer(warrior_tokenid, recepient_pubkey, "1") + token_transfer_txid = rpc_connection.sendrawtransaction(token_transfer_hex["hex"]) + except Exception as e: + print(e) + print("Something went wrong. Please be careful with your input next time!") + input("Press [Enter] to continue...") + break + print(colorize("Warrior succesfully transferred! Transfer txid: " + token_transfer_txid, "green")) + input("Press [Enter] to continue...") + break + if need_transfer == "n": + print("As you wish!") + input("Press [Enter] to continue...") + break + else: + print(colorize("Choose y or n!", "red")) + + +def check_if_config_is_here(rpc_connection, assetchain_name): + config_name = assetchain_name + ".conf" + if os.path.exists(config_name): + print(colorize("Config is already in daemon folder", "green")) + else: + if operating_system == 'Darwin': + path_to_config = os.environ['HOME'] + '/Library/Application Support/Komodo/' + assetchain_name + '/' + config_name + elif operating_system == 'Linux': + path_to_config = os.environ['HOME'] + '/.komodo/' + assetchain_name + '/' + config_name + elif operating_system == 'Win64' or operating_system == 'Windows': + path_to_config = '%s/komodo/' + assetchain_name + '/' + config_name % os.environ['APPDATA'] + try: + copy(path_to_config, os.getcwd()) + except Exception as e: + print(e) + print("Can't copy config to current daemon directory automatically by some reason.") + print("Please copy it manually. It's locating here: " + path_to_config) + + +def find_game_keystrokes_in_log(gametxid): + + operating_system = platform.system() + if operating_system == 'Win64' or operating_system == 'Windows': + p1 = subprocess.Popen(["type", "keystrokes.log"], stdout=subprocess.PIPE, shell=True) + p2 = subprocess.Popen(["findstr", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE, shell=True) + else: + p1 = subprocess.Popen(["cat", "keystrokes.log"], stdout=subprocess.PIPE) + p2 = subprocess.Popen(["grep", gametxid], stdin=p1.stdout, stdout=subprocess.PIPE) + p1.stdout.close() + output = p2.communicate()[0] + keystrokes_log_for_game = bytes.decode(output).split("\n") + return keystrokes_log_for_game + + +def check_if_tx_in_mempool(rpc_connection, txid): + while True: + mempool = rpc_connection.getrawmempool() + if txid in mempool: + print(colorize("Waiting for " + txid + " transaction to be mined", "blue")) + time.sleep(5) + else: + print(colorize("Transaction is mined", "green")) + break diff --git a/src/tui/requirements.txt b/src/tui/requirements.txt new file mode 100644 index 000000000..734da529c --- /dev/null +++ b/src/tui/requirements.txt @@ -0,0 +1,8 @@ +configobj==5.0.6 +pip==9.0.1 +pycurl==7.43.0.2 +setuptools==39.0.1 +six==1.12.0 +slick-bitcoinrpc==0.1.4 +ujson==1.35 +wheel==0.32.3 diff --git a/src/tui/tui_assets.py b/src/tui/tui_assets.py new file mode 100755 index 000000000..091484a40 --- /dev/null +++ b/src/tui/tui_assets.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + + +header = "\ + ___ _ _____ \n\ + / _ \ | | / __ \\\n\ +/ /_\ \ ___ ___ ___ | |_ ___ | / \/\n\ +| _ |/ __|/ __| / _ \| __|/ __|| | \n\ +| | | |\__ \\\__ \| __/| |_ \__ \| \__/\\\n\ +\_| |_/|___/|___/ \___| \__||___/ \____/\n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Print tokens list": tuilib.print_tokens_list}, + {"Check my tokens balances" : tuilib.print_tokens_balances}, + # transfer tokens (pre-print tokens balances) + {"Create token": tuilib.token_create_tui}, + # trading zone - pre-print token orders - possible to open order or fill existing one + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2 by Anton Lysakov\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() + diff --git a/src/tui/tui_gateways_creation.py b/src/tui/tui_gateways_creation.py new file mode 100755 index 000000000..7bb489c7f --- /dev/null +++ b/src/tui/tui_gateways_creation.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + +header = "\ + _____ _ _____ _____ \n\ +| __ \ | | / __ \/ __ \\\n\ +| | \/ __ _| |_ _____ ____ _ _ _ ___| / \/| / \/\n\ +| | __ / _` | __/ _ \ \ /\ / / _` | | | / __| | | | \n\ +| |_\ \ (_| | || __/\ V V / (_| | |_| \__ \ \__/\| \__/\\\n\ + \____/\__,_|\__\___| \_/\_/ \__,_|\__, |___/\____/ \____/\n\ + __/ | \n\ + |___/ \n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Create token": tuilib.token_create_tui}, + {"Create oracle": tuilib.oracle_create_tui}, + {"Register as publisher for oracle": tuilib.oracle_register_tui}, + {"Subscribe on oracle (+UTXO generator)": tuilib.oracle_subscription_utxogen}, + {"Bind Gateway": tuilib.gateways_bind_tui}, + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_gateways_usage.py b/src/tui/tui_gateways_usage.py new file mode 100755 index 000000000..0c989e3af --- /dev/null +++ b/src/tui/tui_gateways_usage.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os, time + +header = "\ + _____ _ _____ _____ \n\ +| __ \ | | / __ \/ __ \\\n\ +| | \/ __ _| |_ _____ ____ _ _ _ ___| / \/| / \/\n\ +| | __ / _` | __/ _ \ \ /\ / / _` | | | / __| | | | \n\ +| |_\ \ (_| | || __/\ V V / (_| | |_| \__ \ \__/\| \__/\\\n\ + \____/\__,_|\__\___| \_/\_/ \__,_|\__, |___/\____/ \____/\n\ + __/ | \n\ + |___/ \n" + +menuItems = [ + {"Check connection to assetchain": tuilib.getinfo_tui}, + {"Check assetchain mempool": tuilib.print_mempool}, + {"Check connection to KMD": tuilib.getinfo_tui}, + {"Connect to KMD daemon": tuilib.rpc_kmd_connection_tui}, + {"Send KMD gateway deposit transaction": tuilib.gateways_send_kmd}, + {"Execute gateways deposit": tuilib.gateways_deposit_tui}, + {"Execute gateways claim": tuilib.gateways_claim_tui}, + {"Execute gateways withdrawal": tuilib.gateways_withdrawal_tui}, + {"Exit": exit} +] + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + # We have to call KMD specific functions with connection to KMD daemon + elif list(menuItems[int(choice)].keys())[0] == "Connect to KMD daemon": + rpc_connection_kmd = list(menuItems[int(choice)].values())[0]() + elif list(menuItems[int(choice)].keys())[0] == "Check connection to KMD": + while True: + try: + list(menuItems[int(choice)].values())[0](rpc_connection_kmd) + break + except Exception as e: + print("Please connect to KMD daemon first!") + input("Press [Enter] to continue...") + break + elif list(menuItems[int(choice)].keys())[0] == "Send KMD gateway deposit transaction": + while True: + try: + list(menuItems[int(choice)].values())[0](rpc_connection_kmd) + break + except Exception as e: + print(e) + print("Please connect to KMD daemon first!") + input("Press [Enter] to continue...") + break + elif list(menuItems[int(choice)].keys())[0] == "Execute gateways deposit": + while True: + try: + list(menuItems[int(choice)].values())[0](rpc_connection, rpc_connection_kmd) + break + except Exception as e: + print(e) + print("Please connect to KMD daemon first!") + input("Press [Enter] to continue...") + break + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\nPlease provide RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_marmara.py b/src/tui/tui_marmara.py new file mode 100755 index 000000000..cfe628890 --- /dev/null +++ b/src/tui/tui_marmara.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + + +header = "\ +___ ___ _____ _ _ _____ \n\ +| \/ | |_ _| | | |_ _|\n\ +| . . | __ _ _ __ _ __ ___ __ _ _ __ __ _ | | | | | | | |\n\ +| |\/| |/ _` | '__| '_ ` _ \ / _` | '__/ _` | | | | | | | | |\n\ +| | | | (_| | | | | | | | | (_| | | | (_| | | | | |_| |_| |_\n\ +\_| |_/\__,_|_| |_| |_| |_|\__,_|_| \__,_| \_/ \___/ \___/\n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Check MARMARA info": tuilib.marmara_info_tui}, + {"Lock funds for MARMARA": tuilib.marmara_lock_tui}, + {"Request MARMARA cheque": tuilib.marmara_receive_tui}, + {"Issue MARMARA cheque": tuilib.marmara_issue_tui}, + {"Check credit loop status": tuilib.marmara_creditloop_tui}, + {"Settle MARMARA loop": tuilib.marmara_settlement_tui}, + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.1\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + chain = input("Input assetchain name (-ac_name= value) you want to work with: ") + try: + print(tuilib.colorize("Welcome to the MarmaraCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.def_credentials(chain) + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_oracles.py b/src/tui/tui_oracles.py new file mode 100755 index 000000000..fec874d35 --- /dev/null +++ b/src/tui/tui_oracles.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time + +header = "\ + _____ _ _____ _____ \n\ +| _ | | | / __ \/ __ \\\n\ +| | | | _ __ __ _ ___ | | ___ ___ | / \/| / \/\n\ +| | | || '__| / _` | / __|| | / _ \/ __|| | | |\n\ +\ \_/ /| | | (_| || (__ | || __/\__ \| \__/\| \__/\\\n\ + \___/ |_| \__,_| \___||_| \___||___/ \____/ \____/\n" + +menuItems = [ + # TODO: Have to implement here native oracle file uploader / reader, should be dope + # TODO: data publisher / converter for different types + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Create oracle": tuilib.oracle_create_tui}, + {"Register as publisher for oracle": tuilib.oracle_register_tui}, + {"Subscribe on oracle (+UTXO generator)": tuilib.oracle_subscription_utxogen}, + {"Upload file to oracle": tuilib.convert_file_oracle_D}, + {"Display list of files uploaded to this AC": tuilib.display_files_list}, + {"Download files from oracle": tuilib.files_downloader}, + {"Exit": exit} +] + + +def main(): + while True: + os.system('clear') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('CLI version 0.2 by Anton Lysakov\n', 'green')) + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + try: + print(tuilib.colorize("Welcome to the GatewaysCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.rpc_connection_tui() + rpclib.getinfo(rpc_connection) + except Exception: + print(tuilib.colorize("Cant connect to RPC! Please re-check credentials.", "pink")) + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_rogue.py b/src/tui/tui_rogue.py new file mode 100755 index 000000000..9942369e2 --- /dev/null +++ b/src/tui/tui_rogue.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time +import sys +import platform + +header = "\ +______ _____ _____ \n\ +| ___ \ / __ \/ __ \\\n\ +| |_/ /___ __ _ _ _ ___| / \/| / \/\n\ +| // _ \ / _` | | | |/ _ \ | | |\n\ +| |\ \ (_) | (_| | |_| | __/ \__/\| \__/\\\n\ +\_| \_\___/ \__, |\__,_|\___|\____/ \____/\n\ + __/ |\n\ + |___/\n" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Check my warriors list": tuilib.print_players_list}, + {"Transfer warrior to other pubkey": tuilib.warrior_trasnfer}, + {"TOP-20 ROGUE Warriors": tuilib.top_warriors_rating}, + {"Set warriors name": tuilib.set_warriors_name}, + {"Start singleplayer training game (creating, registering and starting game)": tuilib.rogue_newgame_singleplayer}, + {"Create multiplayer game": tuilib.rogue_newgame_multiplayer}, + {"Join (register) multiplayer game": tuilib.rogue_join_multiplayer_game}, + {"Check my multiplayer games status / start": tuilib.play_multiplayer_game}, + {"Check if somebody wants to buy your warrior (incoming bids)": tuilib.print_icoming_bids}, + {"Place order to sell warrior": tuilib.sell_warrior}, + {"Place order to buy someones warrior": tuilib.place_bid_on_warriror}, + {"Check if somebody selling warrior": tuilib.find_warriors_asks}, + {"Check / cancel my warriors trade orders": tuilib.warriors_orders_check}, + # {"Manually exit the game (bailout)": "test"}, + # {"Manually claim ROGUE coins for game (highlander)": "test"}, + {"Exit": tuilib.exit} +] + +def main(): + while True: + operating_system = platform.system() + if operating_system != 'Win64' and operating_system != 'Windows': + os.system('clear') + else: + os.system('cls') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('TUI v0.0.3\n', 'green')) + menu_items_counter = 0 + for item in menuItems: + if menu_items_counter == 0: + print("\nUtility:\n") + menu_items_counter = menu_items_counter + 1 + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + if menu_items_counter == 6: + print("\nNew singleplayer game:\n") + if menu_items_counter == 7: + print("\nMultiplayer games:\n") + if menu_items_counter == 10: + print("\nDEX features:\n") + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + chain = "ROGUE" + try: + print(tuilib.colorize("Welcome to the RogueCC TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.def_credentials(chain) + rpclib.getinfo(rpc_connection) + # waiting until chain is in sync + while True: + have_blocks = rpclib.getinfo(rpc_connection)["blocks"] + longest_chain = rpclib.getinfo(rpc_connection)["longestchain"] + if have_blocks != longest_chain: + print(tuilib.colorize("ROGUE not synced yet.", "red")) + print("Have " + str(have_blocks) + " from " + str(longest_chain) + " blocks") + time.sleep(5) + else: + print(tuilib.colorize("Chain is synced!", "green")) + break + # checking if pubkey is set and set valid if not + info = rpclib.getinfo(rpc_connection) + if "pubkey" in info.keys(): + print("Pubkey is already set") + else: + valid_address = rpc_connection.getaccountaddress("") + valid_pubkey = rpc_connection.validateaddress(valid_address)["pubkey"] + rpc_connection.setpubkey(valid_pubkey) + print(tuilib.colorize("Pubkey is succesfully set!", "green")) + # copy ROGUE config to current daemon directory if it's not here + tuilib.check_if_config_is_here(rpc_connection, "ROGUE") + except Exception: + print(tuilib.colorize("Cant connect to ROGUE daemon RPC! Please check if daemon is up.", "pink")) + tuilib.exit() + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/tui/tui_tetris.py b/src/tui/tui_tetris.py new file mode 100755 index 000000000..3c42d4daa --- /dev/null +++ b/src/tui/tui_tetris.py @@ -0,0 +1,96 @@ +#!/usr/bin/env python3 + +from lib import rpclib, tuilib +import os +import time +import sys +import platform + +header = "\ + _____ _ _ ______\n\ +|_ _| | | (_) | _ \n\ + | | ___| |_ _ __ _ ___| | | |__ _ _ __ _ __\n\ + | |/ _ \ __| '__| / __| | | / _` | '_ \| '_ \\\n\ + | | __/ |_| | | \__ \ |/ / (_| | |_) | |_) |\n\ + \_/\___|\__|_| |_|___/___/ \__,_| .__/| .__/\n\ + | | | |\n\ + |_| |_|" + + +menuItems = [ + {"Check current connection": tuilib.getinfo_tui}, + {"Check mempool": tuilib.print_mempool}, + {"Start singleplayer tetris game (creating, registering and starting game)": tuilib.rogue_newgame_singleplayer}, + {"Exit": tuilib.exit} +] + + +def main(): + while True: + operating_system = platform.system() + if operating_system != 'Win64' and operating_system != 'Windows': + os.system('clear') + else: + os.system('cls') + print(tuilib.colorize(header, 'pink')) + print(tuilib.colorize('TUI v0.0.3\n', 'green')) + menu_items_counter = 0 + for item in menuItems: + print(tuilib.colorize("[" + str(menuItems.index(item)) + "] ", 'blue') + list(item.keys())[0]) + choice = input(">> ") + try: + if int(choice) < 0: + raise ValueError + # Call the matching function + if list(menuItems[int(choice)].keys())[0] == "Exit": + list(menuItems[int(choice)].values())[0]() + elif list(menuItems[int(choice)].keys())[0] == "Start singleplayer tetris game (creating, registering and starting game)": + list(menuItems[int(choice)].values())[0](rpc_connection, False) + else: + list(menuItems[int(choice)].values())[0](rpc_connection) + except (ValueError, IndexError): + pass + + +if __name__ == "__main__": + while True: + chain = "GTEST" + try: + print(tuilib.colorize("Welcome to the Tetris TUI!\n" + "Please provide asset chain RPC connection details for initialization", "blue")) + rpc_connection = tuilib.def_credentials(chain) + rpclib.getinfo(rpc_connection) + # waiting until chain is in sync + while True: + have_blocks = rpclib.getinfo(rpc_connection)["blocks"] + longest_chain = rpclib.getinfo(rpc_connection)["longestchain"] + if have_blocks != longest_chain: + print(tuilib.colorize("GTEST not synced yet.", "red")) + print("Have " + str(have_blocks) + " from " + str(longest_chain) + " blocks") + time.sleep(5) + else: + print(tuilib.colorize("Chain is synced!", "green")) + break + # checking if pubkey is set and set valid if not + info = rpclib.getinfo(rpc_connection) + if "pubkey" in info.keys(): + print("Pubkey is already set") + else: + valid_address = rpc_connection.getaccountaddress("") + valid_pubkey = rpc_connection.validateaddress(valid_address)["pubkey"] + rpc_connection.setpubkey(valid_pubkey) + print(tuilib.colorize("Pubkey is succesfully set!", "green")) + # copy ROGUE config to current daemon directory if it's not here + tuilib.check_if_config_is_here(rpc_connection, "GTEST") + except Exception: + print(tuilib.colorize("Cant connect to GTEST daemon RPC! Please check if daemon is up.", "pink")) + tuilib.exit() + else: + print(tuilib.colorize("Succesfully connected!\n", "green")) + with (open("lib/logo.txt", "r")) as logo: + for line in logo: + print(line, end='') + time.sleep(0.04) + print("\n") + break + main() diff --git a/src/txdb.cpp b/src/txdb.cpp index c86ee9bfa..4c9ea31ca 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -60,7 +60,7 @@ static const char DB_LAST_BLOCK = 'l'; CCoinsViewDB::CCoinsViewDB(std::string dbName, size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / dbName, nCacheSize, fMemory, fWipe) { } -CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) +CCoinsViewDB::CCoinsViewDB(size_t nCacheSize, bool fMemory, bool fWipe) : db(GetDataDir() / "chainstate", nCacheSize, fMemory, fWipe) { } @@ -122,7 +122,7 @@ uint256 CCoinsViewDB::GetBestBlock() const { uint256 CCoinsViewDB::GetBestAnchor(ShieldedType type) const { uint256 hashBestAnchor; - + switch (type) { case SPROUT: if (!db.Read(DB_BEST_SPROUT_ANCHOR, hashBestAnchor)) @@ -436,39 +436,35 @@ bool CBlockTreeDB::ReadAddressIndex(uint160 addressHash, int type, } bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &address); +uint32_t komodo_segid32(char *coinaddr); -UniValue CBlockTreeDB::Snapshot(int top) +#define DECLARE_IGNORELIST std::map ignoredMap = { \ + {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, \ + {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, \ + {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, \ + {"RBM5LofZFodMeewUzoMWcxedm3L3hYRaWg", 1}, \ + {"RAdcko2d94TQUcJhtFHZZjMyWBKEVfgn4J", 1}, \ + {"RLzUaZ934k2EFCsAiVjrJqM8uU1vmMRFzk", 1}, \ + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, \ + {"RUDrX1v5toCsJMUgtvBmScKjwCB5NaR8py", 1}, \ + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, \ + {"RRvwmbkxR5YRzPGL5kMFHMe1AH33MeD8rN", 1}, \ + {"RQLQvSgpPAJNPgnpc8MrYsbBhep95nCS8L", 1}, \ + {"RK8JtBV78HdvEPvtV5ckeMPSTojZPzHUTe", 1}, \ + {"RHVs2KaCTGUMNv3cyWiG1jkEvZjigbCnD2", 1}, \ + {"RE3SVaDgdjkRPYA6TRobbthsfCmxQedVgF", 1}, \ + {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, \ + {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, \ + {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} \ +}; + +int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector > &vaddr, UniValue *ret) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; - int64_t utxos = 0; int64_t ignoredAddresses; + int64_t utxos = 0; int64_t ignoredAddresses = 0, cryptoConditionsUTXOs = 0, cryptoConditionsTotals = 0; + DECLARE_IGNORELIST boost::scoped_ptr iter(NewIterator()); std::map addressAmounts; - std::vector > vaddr; - UniValue result(UniValue::VOBJ); - result.push_back(Pair("start_time", (int) time(NULL))); - - std::map ignoredMap = { - {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, - {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, - {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, - {"RBM5LofZFodMeewUzoMWcxedm3L3hYRaWg", 1}, - {"RAdcko2d94TQUcJhtFHZZjMyWBKEVfgn4J", 1}, - {"RLzUaZ934k2EFCsAiVjrJqM8uU1vmMRFzk", 1}, - {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, - {"RUDrX1v5toCsJMUgtvBmScKjwCB5NaR8py", 1}, - {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, - {"RRvwmbkxR5YRzPGL5kMFHMe1AH33MeD8rN", 1}, - {"RQLQvSgpPAJNPgnpc8MrYsbBhep95nCS8L", 1}, - {"RK8JtBV78HdvEPvtV5ckeMPSTojZPzHUTe", 1}, - {"RHVs2KaCTGUMNv3cyWiG1jkEvZjigbCnD2", 1}, - {"RE3SVaDgdjkRPYA6TRobbthsfCmxQedVgF", 1}, - {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, - {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, - {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} //Burnaddress for null privkey - }; - - int64_t startingHeight = chainActive.Height(); - //fprintf(stderr, "Starting snapshot at height %lli\n", startingHeight); for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { boost::this_thread::interruption_point(); @@ -477,97 +473,127 @@ UniValue CBlockTreeDB::Snapshot(int top) std::vector slKey = std::vector(); pair keyObj; iter->GetKey(keyObj); - char chType = keyObj.first; CAddressIndexIteratorKey indexKey = keyObj.second; - //fprintf(stderr, "chType=%d\n", chType); if (chType == DB_ADDRESSUNSPENTINDEX) { try { CAmount nValue; iter->GetValue(nValue); - getAddressFromIndex(indexKey.type, indexKey.hashBytes, address); - - std::map ::iterator ignored = ignoredMap.find(address); - if (ignored != ignoredMap.end()) { - fprintf(stderr,"ignoring %s\n", address.c_str()); - ignoredAddresses++; - continue; + if ( indexKey.type == 3 ) + { + cryptoConditionsUTXOs++; + cryptoConditionsTotals += nValue; + continue; } - - std::map ::iterator pos = addressAmounts.find(address); - if (pos == addressAmounts.end()) { - // insert new address + utxo amount - //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); - addressAmounts[address] = nValue; - totalAddresses++; - } else { - // update unspent tally for this address - //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); - addressAmounts[address] += nValue; - } - //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); - // total += nValue; - utxos++; - } catch (const std::exception& e) { + if ( nValue > dustthreshold ) + { + std::map ::iterator ignored = ignoredMap.find(address); + if (ignored != ignoredMap.end()) + { + fprintf(stderr,"ignoring %s\n", address.c_str()); + ignoredAddresses++; + continue; + } + std::map ::iterator pos = addressAmounts.find(address); + if ( pos == addressAmounts.end() ) + { + // insert new address + utxo amount + //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); + addressAmounts[address] = nValue; + totalAddresses++; + } + else + { + // update unspent tally for this address + //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); + addressAmounts[address] += nValue; + } + //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); + // total += nValue; + utxos++; + } //else fprintf(stderr,"ignoring amount=0 UTXO for %s\n", address.c_str()); + } + catch (const std::exception& e) + { fprintf(stderr, "DONE %s: LevelDB addressindex exception! - %s\n", __func__, e.what()); break; } - } - } catch (const std::exception& e) { - fprintf(stderr, "DONE reading index entries\n"); + } + } + catch (const std::exception& e) + { + fprintf(stderr, "DONE reading index entries\n"); break; } } - - UniValue addresses(UniValue::VARR); //fprintf(stderr, "total=%f, totalAddresses=%li, utxos=%li, ignored=%li\n", (double) total / COIN, totalAddresses, utxos, ignoredAddresses); - - for (std::pair element : addressAmounts) { - vaddr.push_back( make_pair(element.second, element.first) ); - } + for (std::pair element : addressAmounts) + vaddr.push_back( make_pair(element.second, element.first) ); std::sort(vaddr.rbegin(), vaddr.rend()); - - UniValue obj(UniValue::VOBJ); - UniValue addressesSorted(UniValue::VARR); int topN = 0; - for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) { - UniValue obj(UniValue::VOBJ); - obj.push_back( make_pair("addr", it->second.c_str() ) ); - char amount[32]; - sprintf(amount, "%.8f", (double) it->first / COIN); - obj.push_back( make_pair("amount", amount) ); - total += it->first; - addressesSorted.push_back(obj); - topN++; - // If requested, only show top N addresses in output JSON - if (top == topN) - break; + for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) + { + total += it->first; + topN++; + // If requested, only show top N addresses in output JSON + if ( top == topN ) + break; } + // this is for the snapshot RPC, you can skip this by passing a 0 as the last argument. + if (ret) + { + // Total amount in this snapshot, which is less than circulating supply if top parameter is used + // Use the address_total for a total of all address included when using top parameter. + ret->push_back(make_pair("total", (double) (total+cryptoConditionsTotals)/ COIN )); + // Average amount in each address of this snapshot + ret->push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); + // Total number of utxos processed in this snaphot + ret->push_back(make_pair("utxos", utxos)); + // Total number of addresses in this snaphot + ret->push_back(make_pair("total_addresses", top ? top : totalAddresses )); + // Total number of ignored addresses in this snaphot + ret->push_back(make_pair("ignored_addresses", ignoredAddresses)); + // Total number of crypto condition utxos we skipped + ret->push_back(make_pair("skipped_cc_utxos", cryptoConditionsUTXOs)); + // Total value of skipped crypto condition utxos + ret->push_back(make_pair("cc_utxo_value", (double) cryptoConditionsTotals / COIN)); + // total of all the address's, does not count coins in CC vouts. + ret->push_back(make_pair("address_total", (double) total/ COIN )); + // The snapshot finished at this block height + ret->push_back(make_pair("ending_height", chainActive.Height())); + } + return(topN); +} - if (top) - totalAddresses = top; - - if (totalAddresses > 0) { - // Array of all addreses with balances +UniValue CBlockTreeDB::Snapshot(int top) +{ + int topN = 0; + std::vector > vaddr; + UniValue result(UniValue::VOBJ); + UniValue addressesSorted(UniValue::VARR); + result.push_back(Pair("start_time", (int) time(NULL))); + if ( Snapshot2(0,top,vaddr,&result) != 0 ) + { + for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) + { + UniValue obj(UniValue::VOBJ); + obj.push_back( make_pair("addr", it->second.c_str() ) ); + char amount[32]; + sprintf(amount, "%.8f", (double) it->first / COIN); + obj.push_back( make_pair("amount", amount) ); + obj.push_back( make_pair("segid",(int32_t)komodo_segid32((char *)it->second.c_str()) & 0x3f) ); + addressesSorted.push_back(obj); + topN++; + // If requested, only show top N addresses in output JSON + if ( top == topN ) + break; + } + // Array of all addreses with balances result.push_back(make_pair("addresses", addressesSorted)); - // Total amount in this snapshot, which is less than circulating supply if top parameter is used - result.push_back(make_pair("total", (double) total / COIN )); - // Average amount in each address of this snapshot - result.push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); - } - // Total number of utxos processed in this snaphot - result.push_back(make_pair("utxos", utxos)); - // Total number of addresses in this snaphot - result.push_back(make_pair("total_addresses", totalAddresses)); - // Total number of ignored addresses in this snaphot - result.push_back(make_pair("ignored_addresses", ignoredAddresses)); - // The snapshot began at this block height - result.push_back(make_pair("start_height", startingHeight)); - // The snapshot finished at this block height - result.push_back(make_pair("ending_height", chainActive.Height())); + } else result.push_back(make_pair("error", "problem doing snapshot")); return(result); } diff --git a/src/txdb.h b/src/txdb.h index b9bae2fe4..b4c4cd6bd 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -116,6 +116,7 @@ public: bool LoadBlockIndexGuts(); bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); + int32_t Snapshot2(int64_t dustthreshold, int32_t top,std::vector > &vaddr, UniValue *ret); }; #endif // BITCOIN_TXDB_H diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f57860c76..2b38d7153 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -160,7 +160,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC if (vDest.which()) { uint160 hashBytes; - if (CBitcoinAddress(vDest).GetIndexKey(hashBytes, keyType)) + if (CBitcoinAddress(vDest).GetIndexKey(hashBytes, keyType, prevout.scriptPubKey.IsPayToCryptoCondition())) { vSols.push_back(vector(hashBytes.begin(), hashBytes.end())); } @@ -192,7 +192,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC if (vDest.which()) { uint160 hashBytes; - if (CBitcoinAddress(vDest).GetIndexKey(hashBytes, keyType)) + if (CBitcoinAddress(vDest).GetIndexKey(hashBytes, keyType, out.scriptPubKey.IsPayToCryptoCondition())) { vSols.push_back(vector(hashBytes.begin(), hashBytes.end())); } diff --git a/src/uthash.h b/src/uthash.h old mode 100755 new mode 100644 diff --git a/src/util.cpp b/src/util.cpp index 2abbf5bef..980d82ac9 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -394,6 +394,30 @@ void ParseParameters(int argc, const char* const argv[]) } } +void SplitStr(const std::string& strVal, std::vector &outVals) +{ + stringstream ss(strVal); + std::string str; + + while ( ss.peek() == ' ' ) + ss.ignore(); + + while ( ss >> str ) + { + if ( str.size() == 0 ) + continue; + if ( str[str.size()-1] == ',' ) + str.resize(str.size()-1); + outVals.push_back(str); + while ( ss.peek() == ' ' ) + ss.ignore(); + if ( ss.peek() == ',' ) + ss.ignore(); + while ( ss.peek() == ' ' ) + ss.ignore(); + } +} + void Split(const std::string& strVal, uint64_t *outVals, const uint64_t nDefault) { stringstream ss(strVal); diff --git a/src/utilstrencodings.cpp b/src/utilstrencodings.cpp index ece2ec73e..d4bba72ee 100644 --- a/src/utilstrencodings.cpp +++ b/src/utilstrencodings.cpp @@ -419,10 +419,26 @@ bool ParseFixedPoint(const std::string &val, int decimals, int64_t *amount_out) int mantissa_tzeros = 0; bool mantissa_sign = false; bool exponent_sign = false; - int ptr = 0; + int i,n,ptr = 0; char *str = (char *)val.c_str(); int end = val.size(); int point_ofs = 0; - + n = val.size(); + if ( n == 11 && (val[0] < '9' || val[1] < '3') ) + { + uint64_t val64 = 0; + for (i=0; i '9' ) + break; + val64 = (val64 * 10) + (val[i] - '0'); + } + if ( i == n ) // 90000000000 + { + *amount_out = val64 * 100000000; + //fprintf(stderr,"special case: %s -> %.8f\n",val.c_str(),(double)*amount_out/100000000); + return(true); + } + } if (ptr < end && val[ptr] == '-') { mantissa_sign = true; ++ptr; diff --git a/src/utlist.h b/src/utlist.h old mode 100755 new mode 100644 diff --git a/src/version.h b/src/version.h index 8b46fe7cf..64150b740 100644 --- a/src/version.h +++ b/src/version.h @@ -34,6 +34,7 @@ static const int GETHEADERS_VERSION = 31800; //! disconnect from peers older than this proto version static const int MIN_PEER_PROTO_VERSION = 170002; +static const int STAKEDMIN_PEER_PROTO_VERSION = 170007; //! nTime field added to CAddress, starting with this version; //! if possible, avoid requesting addresses nodes older than this diff --git a/src/wallet-utility.cpp b/src/wallet-utility.cpp index 7af2ca5bf..655a3cab4 100644 --- a/src/wallet-utility.cpp +++ b/src/wallet-utility.cpp @@ -18,6 +18,7 @@ uint32_t ASSETCHAIN_INIT,ASSETCHAINS_CC; uint32_t ASSETCHAINS_MAGIC = 2387029918; uint32_t ASSETCHAINS_EQUIHASH = 0; uint32_t ASSETCHAINS_VERUSHASH = 1; +uint32_t ASSETCHAINS_VERUSHASHV1_1 = 2; uint32_t ASSETCHAINS_ALGO = 0; int32_t ASSETCHAINS_LWMAPOS = 0; int32_t VERUS_BLOCK_POSUNITS = 1000; diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index d4c5077cc..0ea17a5f8 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -54,6 +54,8 @@ using namespace libzcash; extern char ASSETCHAINS_SYMBOL[65]; +int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); +int tx_height( const uint256 &hash ); extern UniValue signrawtransaction(const UniValue& params, bool fHelp); extern UniValue sendrawtransaction(const UniValue& params, bool fHelp); @@ -1049,8 +1051,16 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) { continue; } - if (out.nDepth < mindepth_) { - continue; + if( mindepth_ > 1 ) { + int nHeight = tx_height(out.tx->GetHash()); + int dpowconfs = komodo_dpowconfs(nHeight, out.nDepth); + if (dpowconfs < mindepth_) { + continue; + } + } else { + if (out.nDepth < mindepth_) { + continue; + } } const CScript &scriptPubKey = out.tx->vout[out.i].scriptPubKey; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 14620aa0b..a21de63f4 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -37,6 +37,7 @@ #include "zcbenchmarks.h" #include "script/interpreter.h" #include "zcash/zip32.h" +#include "notaries_staked.h" #include "utiltime.h" #include "asyncrpcoperation.h" @@ -64,9 +65,9 @@ using namespace std; using namespace libzcash; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; +extern std::string ASSETCHAINS_OVERRIDE_PUBKEY; const std::string ADDR_TYPE_SPROUT = "sprout"; const std::string ADDR_TYPE_SAPLING = "sapling"; - extern UniValue TxJoinSplitToJSON(const CTransaction& tx); uint32_t komodo_segid32(char *coinaddr); int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); @@ -83,6 +84,8 @@ UniValue z_getoperationstatus_IMPL(const UniValue&, bool); #define PLAN_NAME_MAX 8 #define VALID_PLAN_NAME(x) (strlen(x) <= PLAN_NAME_MAX) +int tx_height( const uint256 &hash ); + std::string HelpRequiringPassphrase() { return pwalletMain && pwalletMain->IsCrypted() @@ -420,7 +423,7 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr // Check amount if (nValue <= 0) throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); - +//fprintf(stderr,"nValue %.8f vs curBalance %.8f\n",(double)nValue/COIN,(double)curBalance/COIN); if (nValue > curBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); @@ -932,9 +935,20 @@ UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) continue; BOOST_FOREACH(const CTxOut& txout, wtx.vout) - if (txout.scriptPubKey == scriptPubKey) - if (wtx.GetDepthInMainChain() >= nMinDepth) - nAmount += txout.nValue; // komodo_interest? + if (txout.scriptPubKey == scriptPubKey) { + int nDepth = wtx.GetDepthInMainChain(); + if( nMinDepth > 1 ) { + int nHeight = tx_height(wtx.GetHash()); + int dpowconfs = komodo_dpowconfs(nHeight, nDepth); + if (dpowconfs >= nMinDepth) { + nAmount += txout.nValue; // komodo_interest? + } + } else { + if (nDepth >= nMinDepth) { + nAmount += txout.nValue; // komodo_interest? + } + } + } } return ValueFromAmount(nAmount); @@ -1012,8 +1026,18 @@ CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMi CAmount nReceived, nSent, nFee; wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); - if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) - nBalance += nReceived; + int nDepth = wtx.GetDepthInMainChain(); + if( nMinDepth > 1 ) { + int nHeight = tx_height(wtx.GetHash()); + int dpowconfs = komodo_dpowconfs(nHeight, nDepth); + if (nReceived != 0 && dpowconfs >= nMinDepth) { + nBalance += nReceived; + } + } else { + if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) { + nBalance += nReceived; + } + } nBalance -= nSent + nFee; } @@ -1029,6 +1053,108 @@ CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminef return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); } +UniValue cleanwallettransactions(const UniValue& params, bool fHelp) +{ + if (!EnsureWalletIsAvailable(fHelp)) + return NullUniValue; + + if (fHelp || params.size() > 1 ) + throw runtime_error( + "cleanwallettransactions \"txid\"\n" + "\nRemove all txs that are spent. You can clear all txs bar one, by specifiying a txid.\n" + "\nPlease backup your wallet.dat before running this command.\n" + "\nArguments:\n" + "1. \"txid\" (string, optional) The transaction id to keep.\n" + "\nResult:\n" + "{\n" + " \"total_transactions\" : n, (numeric) Transactions in wallet of " + strprintf("%s",komodo_chainname()) + "\n" + " \"remaining_transactions\" : n, (numeric) Transactions in wallet after clean.\n" + " \"removed_transactions\" : n, (numeric) The number of transactions removed.\n" + "}\n" + "\nExamples:\n" + + HelpExampleCli("cleanwallettransactions", "") + + HelpExampleCli("cleanwallettransactions","\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + + HelpExampleRpc("cleanwallettransactions", "") + + HelpExampleRpc("cleanwallettransactions","\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") + ); + + LOCK2(cs_main, pwalletMain->cs_wallet); + UniValue ret(UniValue::VOBJ); + uint256 exception; int32_t txs = pwalletMain->mapWallet.size(); + std::vector TxToRemove; + if (params.size() == 1) + { + exception.SetHex(params[0].get_str()); + uint256 tmp_hash; CTransaction tmp_tx; + if (GetTransaction(exception,tmp_tx,tmp_hash,false)) + { + if ( !pwalletMain->IsMine(tmp_tx) ) + { + throw runtime_error("\nThe transaction is not yours!\n"); + } + else + { + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if ( wtx.GetHash() != exception ) + { + TxToRemove.push_back(wtx.GetHash()); + } + } + } + } + else + { + throw runtime_error("\nThe transaction could not be found!\n"); + } + } + else + { + // get all locked utxos to relock them later. + vector vLockedUTXO; + pwalletMain->ListLockedCoins(vLockedUTXO); + // unlock all coins so that the following call containes all utxos. + pwalletMain->UnlockAllCoins(); + // listunspent call... this gets us all the txids that are unspent, we search this list for the oldest tx, + vector vecOutputs; + assert(pwalletMain != NULL); + pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); + int32_t oldestTxDepth = 0; + BOOST_FOREACH(const COutput& out, vecOutputs) + { + if ( out.nDepth > oldestTxDepth ) + oldestTxDepth = out.nDepth; + } + oldestTxDepth = oldestTxDepth + 1; // add extra block just for safety. + // lock all the previouly locked coins. + BOOST_FOREACH(COutPoint &outpt, vLockedUTXO) { + pwalletMain->LockCoin(outpt); + } + + // then add all txs in the wallet before this block to the list to remove. + for (map::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) + { + const CWalletTx& wtx = (*it).second; + if (wtx.GetDepthInMainChain() > oldestTxDepth) + TxToRemove.push_back(wtx.GetHash()); + } + } + + // erase txs + BOOST_FOREACH (uint256& hash, TxToRemove) + { + pwalletMain->EraseFromWallet(hash); + LogPrintf("Erased %s from wallet.\n",hash.ToString().c_str()); + } + + // build return JSON for stats. + int remaining = pwalletMain->mapWallet.size(); + ret.push_back(Pair("total_transactons", (int)txs)); + ret.push_back(Pair("remaining_transactons", (int)remaining)); + ret.push_back(Pair("removed_transactions", (int)(txs-remaining))); + return (ret); +} UniValue getbalance(const UniValue& params, bool fHelp) { @@ -1083,10 +1209,20 @@ UniValue getbalance(const UniValue& params, bool fHelp) list listReceived; list listSent; wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); - if (wtx.GetDepthInMainChain() >= nMinDepth) - { - BOOST_FOREACH(const COutputEntry& r, listReceived) - nBalance += r.amount; + + int nDepth = wtx.GetDepthInMainChain(); + if( nMinDepth > 1 ) { + int nHeight = tx_height(wtx.GetHash()); + int dpowconfs = komodo_dpowconfs(nHeight, nDepth); + if (dpowconfs >= nMinDepth) { + BOOST_FOREACH(const COutputEntry& r, listReceived) + nBalance += r.amount; + } + } else { + if (nDepth >= nMinDepth) { + BOOST_FOREACH(const COutputEntry& r, listReceived) + nBalance += r.amount; + } } BOOST_FOREACH(const COutputEntry& s, listSent) nBalance -= s.amount; @@ -1321,24 +1457,19 @@ UniValue sendmany(const UniValue& params, bool fHelp) if (params.size() > 4) subtractFeeFromAmount = params[4].get_array(); - std::set destinations; std::vector vecSend; CAmount totalAmount = 0; std::vector keys = sendTo.getKeys(); + int32_t i = 0; for (const std::string& name_ : keys) { CTxDestination dest = DecodeDestination(name_); if (!IsValidDestination(dest)) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, std::string("Invalid Zcash address: ") + name_); } - /*if (destinations.count(dest)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, std::string("Invalid parameter, duplicated address: ") + name_); - }*/ - destinations.insert(dest); - CScript scriptPubKey = GetScriptForDestination(dest); - CAmount nAmount = AmountFromValue(sendTo[name_]); + CAmount nAmount = AmountFromValue(sendTo[i]); if (nAmount <= 0) throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); totalAmount += nAmount; @@ -1352,13 +1483,13 @@ UniValue sendmany(const UniValue& params, bool fHelp) CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; vecSend.push_back(recipient); + i++; } EnsureWalletIsUnlocked(); // Check funds - CAmount nBalance = pwalletMain->GetBalance(); - //CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); + CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); if (totalAmount > nBalance) throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); @@ -1469,9 +1600,16 @@ UniValue ListReceived(const UniValue& params, bool fByAccounts) if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) continue; - int nDepth = wtx.GetDepthInMainChain(); - if (nDepth < nMinDepth) - continue; + int nDepth = wtx.GetDepthInMainChain(); + if( nMinDepth > 1 ) { + int nHeight = tx_height(wtx.GetHash()); + int dpowconfs = komodo_dpowconfs(nHeight, nDepth); + if (dpowconfs < nMinDepth) + continue; + } else { + if (nDepth < nMinDepth) + continue; + } BOOST_FOREACH(const CTxOut& txout, wtx.vout) { @@ -2671,6 +2809,8 @@ UniValue resendwallettransactions(const UniValue& params, bool fHelp) return result; } +extern uint32_t komodo_segid32(char *coinaddr); + UniValue listunspent(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -2746,8 +2886,16 @@ UniValue listunspent(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); BOOST_FOREACH(const COutput& out, vecOutputs) { - if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) - continue; + int nDepth = out.tx->GetDepthInMainChain(); + if( nMinDepth > 1 ) { + int nHeight = tx_height(out.tx->GetHash()); + int dpowconfs = komodo_dpowconfs(nHeight, nDepth); + if (dpowconfs < nMinDepth || dpowconfs > nMaxDepth) + continue; + } else { + if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) + continue; + } CTxDestination address; const CScript& scriptPubKey = out.tx->vout[out.i].scriptPubKey; @@ -2765,6 +2913,7 @@ UniValue listunspent(const UniValue& params, bool fHelp) if (fValidAddress) { entry.push_back(Pair("address", EncodeDestination(address))); + entry.push_back(Pair("segid", (int)komodo_segid32((char*)EncodeDestination(address).c_str()) & 0x3f )); if (pwalletMain->mapAddressBook.count(address)) entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); @@ -2878,7 +3027,7 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) "\nExamples\n" + HelpExampleCli("z_listunspent", "") + HelpExampleCli("z_listunspent", "6 9999999 false \"[\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\",\\\"zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37\\\"]\"") - + HelpExampleRpc("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)); @@ -2962,28 +3111,17 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) for (auto & entry : sproutEntries) { UniValue obj(UniValue::VOBJ); - int nHeight = 0; - CTransaction tx; - uint256 hashBlock; + + int nHeight = tx_height(entry.jsop.hash); + int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations); + // Only return notarized results when minconf>1 + if (nMinDepth > 1 && dpowconfs == 1) + continue; obj.push_back(Pair("txid", entry.jsop.hash.ToString())); obj.push_back(Pair("jsindex", (int)entry.jsop.js )); obj.push_back(Pair("jsoutindex", (int)entry.jsop.n)); - - if (!GetTransaction(entry.jsop.hash, tx, hashBlock, true)) { - // TODO: should we throw JSONRPCError ? - fprintf(stderr,"tx hash %s does not exist!\n", entry.jsop.hash.ToString().c_str() ); - } - - BlockMap::const_iterator it = mapBlockIndex.find(hashBlock); - if (it != mapBlockIndex.end()) { - nHeight = it->second->GetHeight(); - //fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight); - } else { - // TODO: should we throw JSONRPCError ? - fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() ); - } - obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations))); + obj.push_back(Pair("confirmations", dpowconfs)); obj.push_back(Pair("rawconfirmations", entry.confirmations)); bool hasSproutSpendingKey = pwalletMain->HaveSproutSpendingKey(boost::get(entry.address)); obj.push_back(Pair("spendable", hasSproutSpendingKey)); @@ -2999,25 +3137,17 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) for (auto & entry : saplingEntries) { UniValue obj(UniValue::VOBJ); + + int nHeight = tx_height(entry.op.hash); + int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations); + + // Only return notarized results when minconf>1 + if (nMinDepth > 1 && dpowconfs == 1) + continue; + obj.push_back(Pair("txid", entry.op.hash.ToString())); obj.push_back(Pair("outindex", (int)entry.op.n)); - int nHeight = 0; - CTransaction tx; - uint256 hashBlock; - if (!GetTransaction(entry.op.hash, tx, hashBlock, true)) { - // TODO: should we throw JSONRPCError ? - fprintf(stderr,"tx hash %s does not exist!\n", entry.op.hash.ToString().c_str() ); - } - - BlockMap::const_iterator it = mapBlockIndex.find(hashBlock); - if (it != mapBlockIndex.end()) { - nHeight = it->second->GetHeight(); - //fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight); - } else { - // TODO: should we throw JSONRPCError ? - fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() ); - } - obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations))); + obj.push_back(Pair("confirmations", dpowconfs)); obj.push_back(Pair("rawconfirmations", entry.confirmations)); libzcash::SaplingIncomingViewingKey ivk; libzcash::SaplingFullViewingKey fvk; @@ -3038,7 +3168,6 @@ UniValue z_listunspent(const UniValue& params, bool fHelp) return results; } - UniValue fundrawtransaction(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp)) @@ -3689,8 +3818,17 @@ CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ign pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); BOOST_FOREACH(const COutput& out, vecOutputs) { - if (out.nDepth < minDepth) { - continue; + int nDepth = out.tx->GetDepthInMainChain(); + if( minDepth > 1 ) { + int nHeight = tx_height(out.tx->GetHash()); + int dpowconfs = komodo_dpowconfs(nHeight, nDepth); + if (dpowconfs < minDepth) { + continue; + } + } else { + if (out.nDepth < minDepth) { + continue; + } } if (ignoreUnspendable && !out.fSpendable) { @@ -3795,21 +3933,11 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) if (boost::get(&zaddr) != nullptr) { for (CSproutNotePlaintextEntry & entry : sproutEntries) { UniValue obj(UniValue::VOBJ); - int nHeight = 0; - CTransaction tx; - uint256 hashBlock; - - if (GetTransaction(entry.jsop.hash, tx, hashBlock, true)) { - BlockMap::const_iterator it = mapBlockIndex.find(hashBlock); - if (it != mapBlockIndex.end()) { - nHeight = it->second->GetHeight(); - //fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight); - } else { - fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() ); - } - } else { - fprintf(stderr,"tx hash %s does not exist!\n", entry.jsop.hash.ToString().c_str() ); - } + int nHeight = tx_height(entry.jsop.hash); + int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations); + // Only return notarized results when minconf>1 + if (nMinDepth > 1 && dpowconfs == 1) + continue; obj.push_back(Pair("txid", entry.jsop.hash.ToString())); obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value())))); @@ -3818,7 +3946,7 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) obj.push_back(Pair("jsindex", entry.jsop.js)); obj.push_back(Pair("jsoutindex", entry.jsop.n)); obj.push_back(Pair("rawconfirmations", entry.confirmations)); - obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations))); + obj.push_back(Pair("confirmations", dpowconfs)); if (hasSpendingKey) { obj.push_back(Pair("change", pwalletMain->IsNoteSproutChange(nullifierSet, entry.address, entry.jsop))); } @@ -3827,27 +3955,19 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) } else if (boost::get(&zaddr) != nullptr) { for (SaplingNoteEntry & entry : saplingEntries) { UniValue obj(UniValue::VOBJ); - int nHeight = 0; - CTransaction tx; - uint256 hashBlock; - if (GetTransaction(entry.op.hash, tx, hashBlock, true)) { - BlockMap::const_iterator it = mapBlockIndex.find(hashBlock); - if (it != mapBlockIndex.end()) { - nHeight = it->second->GetHeight(); - //fprintf(stderr,"blockHash %s height %d\n",hashBlock.ToString().c_str(), nHeight); - } else { - fprintf(stderr,"block hash %s does not exist!\n", hashBlock.ToString().c_str() ); - } - } else { - fprintf(stderr,"tx hash %s does not exist!\n", entry.op.hash.ToString().c_str() ); - } + int nHeight = tx_height(entry.op.hash); + int dpowconfs = komodo_dpowconfs(nHeight, entry.confirmations); + // Only return notarized results when minconf>1 + if (nMinDepth > 1 && dpowconfs == 1) + continue; + obj.push_back(Pair("txid", entry.op.hash.ToString())); obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.note.value())))); obj.push_back(Pair("memo", HexStr(entry.memo))); obj.push_back(Pair("outindex", (int)entry.op.n)); obj.push_back(Pair("rawconfirmations", entry.confirmations)); - obj.push_back(Pair("confirmations", komodo_dpowconfs(nHeight, entry.confirmations))); + obj.push_back(Pair("confirmations", dpowconfs)); if (hasSpendingKey) { obj.push_back(Pair("change", pwalletMain->IsNoteSaplingChange(nullifierSet, entry.address, entry.op))); } @@ -4640,54 +4760,56 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) strDisabledMsg = experimentalDisabledHelpMsg("z_mergetoaddress", enableArg); } - if (fHelp || params.size() < 2 || params.size() > 6) + if (fHelp || params.size() < 2 || params.size() > 7) 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" - " - \"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\"") - ); + "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 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. maximum_utxo_size (numeric, optional) eg, 0.0001 anything under 10000 satoshies will be merged, ignores 10,000 sat p2pk utxo that iguana uses, and merges coinbase utxo.\n" + "6. \"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\"") + ); if (!fEnableMergeToAddress) { throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_mergetoaddress is disabled."); @@ -4802,9 +4924,19 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) saplingNoteLimit = nNoteLimit; } - std::string memo; + CAmount maximum_utxo_size; if (params.size() > 5) { - memo = params[5].get_str(); + maximum_utxo_size = AmountFromValue( params[5] ); + if (maximum_utxo_size < 10) { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Maximum size must be bigger than 0.00000010."); + } + } else { + maximum_utxo_size = 0; + } + + std::string memo; + if (params.size() > 6) { + memo = params[6].get_str(); 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)) { @@ -4842,7 +4974,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) if (useAnyUTXO || taddrs.size() > 0) { // Get available utxos vector vecOutputs; - pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false); + pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, maximum_utxo_size != 0 ? true : false); // Find unspent utxos and update estimated size for (const COutput& out : vecOutputs) { @@ -4861,9 +4993,18 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) continue; } - utxoCounter++; CAmount nValue = out.tx->vout[out.i].nValue; + if (maximum_utxo_size != 0) + { + //fprintf(stderr, "utxo txid.%s vout.%i nValue.%li scriptpubkeylength.%i\n",out.tx->GetHash().ToString().c_str(),out.i,nValue,out.tx->vout[out.i].scriptPubKey.size()); + if (nValue > maximum_utxo_size) + continue; + if (nValue == 10000 && out.tx->vout[out.i].scriptPubKey.size() == 35) + continue; + } + + utxoCounter++; if (!maxedOutUTXOsFlag) { size_t increase = (boost::get(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; if (estimatedTxSize + increase >= max_tx_size || @@ -4965,10 +5106,11 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) size_t numUtxos = utxoInputs.size(); size_t numNotes = sproutNoteInputs.size() + saplingNoteInputs.size(); - if (numUtxos == 0 && numNotes == 0) { + //fprintf(stderr, "num utxos.%li\n", numUtxos); + if (numUtxos < 2 && 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 @@ -5009,7 +5151,9 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) boost::optional builder; if (isToSaplingZaddr || saplingNoteInputs.size() > 0) { builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); - } + } else + contextualTx.nExpiryHeight = 0; // set non z-tx to have no expiry height. + // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); std::shared_ptr operation( @@ -5082,9 +5226,6 @@ UniValue z_listoperationids(const UniValue& params, bool fHelp) #include "script/sign.h" int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); extern std::string NOTARY_PUBKEY; -uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 hash,int32_t n,uint32_t blocktime,uint32_t prevtime,char *destaddr); -int8_t komodo_stakehash(uint256 *hashp,char *address,uint8_t *hashbuf,uint256 txid,int32_t vout); -void komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n); int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) { @@ -5145,8 +5286,8 @@ int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) // ((uint8_t *)&revtxid)[i] = ((uint8_t *)&utxotxid)[31 - i]; txNew.vin[0].prevout.hash = utxotxid; //revtxid; txNew.vin[0].prevout.n = utxovout; - txNew.vout[0].scriptPubKey = CScript() << ParseHex(CRYPTO777_PUBSECPSTR) << OP_CHECKSIG; txNew.vout[0].nValue = utxovalue - txfee; + txNew.vout[0].scriptPubKey = CScript() << ParseHex(CRYPTO777_PUBSECPSTR) << OP_CHECKSIG; CTransaction txNewConst(txNew); signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, utxovalue, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); if (!signSuccess) @@ -5170,17 +5311,6 @@ int32_t verus_staked(CBlock *pBlock, CMutableTransaction &txNew, uint32_t &nBits return pwalletMain->VerusStakeTransaction(pBlock, txNew, nBits, hashResult, utxosig, pk); } -int32_t ensure_CCrequirements() -{ - CCerror = ""; - if ( NOTARY_PUBKEY33[0] == 0 ) - return(-1); - else if ( GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) == 0 ) - return(-1); - else if ( GetBoolArg("-spentindex", DEFAULT_SPENTINDEX) == 0 ) - return(-1); - else return(0); -} #include "../cc/CCfaucet.h" #include "../cc/CCassets.h" @@ -5195,10 +5325,37 @@ int32_t ensure_CCrequirements() #include "../cc/CCPrices.h" #include "../cc/CCHeir.h" #include "../cc/CCMarmara.h" +#include "../cc/CCPayments.h" + +int32_t ensure_CCrequirements(uint8_t evalcode) +{ + CCerror = ""; + if ( ASSETCHAINS_CCDISABLES[evalcode] != 0 || (evalcode == EVAL_MARMARA && ASSETCHAINS_MARMARA == 0) ) + { + fprintf(stderr,"evalcode %d disabled\n",evalcode); + return(-1); + } + if ( NOTARY_PUBKEY33[0] == 0 ) + { + fprintf(stderr,"no -pubkey set\n"); + return(-1); + } + else if ( GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) == 0 ) + { + fprintf(stderr,"no -addressindex\n"); + return(-1); + } + else if ( GetBoolArg("-spentindex", DEFAULT_SPENTINDEX) == 0 ) + { + fprintf(stderr,"no -spentindex\n"); + return(-1); + } + else return(0); +} UniValue CCaddress(struct CCcontract_info *cp,char *name,std::vector &pubkey) { - UniValue result(UniValue::VOBJ); char destaddr[64],str[64]; CPubKey pk; + UniValue result(UniValue::VOBJ); char destaddr[64],str[64]; CPubKey mypk,pk; pk = GetUnspendable(cp,0); GetCCaddress(cp,destaddr,pk); if ( strcmp(destaddr,cp->unspendableCCaddr) != 0 ) @@ -5209,34 +5366,69 @@ UniValue CCaddress(struct CCcontract_info *cp,char *name,std::vectorunspendableCCaddr,destaddr); } result.push_back(Pair("result", "success")); - sprintf(str,"%sCCaddress",name); + sprintf(str,"%sCCAddress",name); result.push_back(Pair(str,cp->unspendableCCaddr)); - sprintf(str,"%smarker",name); + sprintf(str,"%sCCBalance",name); + result.push_back(Pair(str,ValueFromAmount(CCaddress_balance(cp->unspendableCCaddr,1)))); + sprintf(str,"%sNormalAddress",name); result.push_back(Pair(str,cp->normaladdr)); - result.push_back(Pair("GatewaysPubkey","03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40")); - if ( _GetCCaddress(destaddr,EVAL_ASSETS,pubkey2pk(pubkey)) > 0 ) + sprintf(str,"%sNormalBalance",name); + result.push_back(Pair(str,ValueFromAmount(CCaddress_balance(cp->normaladdr,0)))); + if (strcmp(name,"Gateways")==0) result.push_back(Pair("GatewaysPubkey","03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40")); + if ((strcmp(name,"Channels")==0 || strcmp(name,"Heir")==0) && pubkey.size() == 33) { - sprintf(str,"%sCCassets",name); + sprintf(str,"%sCC1of2Address",name); + mypk = pubkey2pk(Mypubkey()); + GetCCaddress1of2(cp,destaddr,mypk,pubkey2pk(pubkey)); result.push_back(Pair(str,destaddr)); + if (GetTokensCCaddress1of2(cp,destaddr,mypk,pubkey2pk(pubkey))>0) + { + sprintf(str,"%sCC1of2TokensAddress",name); + result.push_back(Pair(str,destaddr)); + } + } + else if (strcmp(name,"Tokens")!=0) + { + if (GetTokensCCaddress(cp,destaddr,pk)>0) + { + sprintf(str,"%sCCTokensAddress",name); + result.push_back(Pair(str,destaddr)); + } } if ( pubkey.size() == 33 ) { if ( GetCCaddress(cp,destaddr,pubkey2pk(pubkey)) != 0 ) - result.push_back(Pair("CCaddress",destaddr)); + { + sprintf(str,"PubkeyCCaddress(%s)",name); + result.push_back(Pair(str,destaddr)); + sprintf(str,"PubkeyCCbalance(%s)",name); + result.push_back(Pair(str,ValueFromAmount(CCaddress_balance(destaddr,0)))); + } } if ( GetCCaddress(cp,destaddr,pubkey2pk(Mypubkey())) != 0 ) - result.push_back(Pair("myCCaddress",destaddr)); + { + sprintf(str,"myCCAddress(%s)",name); + result.push_back(Pair(str,destaddr)); + sprintf(str,"myCCbalance(%s)",name); + result.push_back(Pair(str,ValueFromAmount(CCaddress_balance(destaddr,1)))); + } if ( Getscriptaddress(destaddr,(CScript() << Mypubkey() << OP_CHECKSIG)) != 0 ) + { result.push_back(Pair("myaddress",destaddr)); + result.push_back(Pair("mybalance",ValueFromAmount(CCaddress_balance(destaddr,0)))); + } return(result); } bool pubkey2addr(char *destaddr,uint8_t *pubkey33); +extern int32_t IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY; +extern uint8_t NOTARY_PUBKEY33[]; +extern std::string NOTARY_PUBKEY,NOTARY_ADDRESS; UniValue setpubkey(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); - if ( fHelp || params.size() != 1 ) + if ( fHelp || params.size() > 1 ) throw runtime_error( "setpubkey\n" "\nSets the -pubkey if the daemon was not started with it, if it was already set, it returns the pubkey, and its Raddress.\n" @@ -5253,112 +5445,213 @@ UniValue setpubkey(const UniValue& params, bool fHelp) + HelpExampleRpc("setpubkey", "02f7597468703c1c5c8465dd6d43acaae697df9df30bed21494d193412a1ea193e") ); -#ifdef ENABLE_WALLET LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); -#else - LOCK(cs_main); -#endif - char Raddress[18]; + char Raddress[64]; uint8_t pubkey33[33]; - if ( NOTARY_PUBKEY33[0] == 0 ) { - if (strlen(params[0].get_str().c_str()) == 66) { + if ( NOTARY_PUBKEY33[0] == 0 ) + { + if (strlen(params[0].get_str().c_str()) == 66) + { decode_hex(pubkey33,33,(char *)params[0].get_str().c_str()); pubkey2addr((char *)Raddress,(uint8_t *)pubkey33); - if (strcmp("RRmWExvapDM9YbLT9X9xAyzDgxomYf63ng",Raddress) == 0) { - result.push_back(Pair("error", "pubkey entered is invalid.")); - } else { - CBitcoinAddress address(Raddress); - bool isValid = address.IsValid(); - if (isValid) + CBitcoinAddress address(Raddress); + bool isValid = address.IsValid(); + if (isValid) + { + CTxDestination dest = address.Get(); + isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; + if ( mine == ISMINE_NO ) + result.push_back(Pair("WARNING", "privkey for this pubkey is not imported to wallet!")); + else { - CTxDestination dest = address.Get(); - string currentAddress = address.ToString(); - result.push_back(Pair("address", currentAddress)); -#ifdef ENABLE_WALLET - isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; - result.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); -#endif + result.push_back(Pair("ismine", "true")); + std::string notaryname; + if ( (IS_STAKED_NOTARY= StakedNotaryID(notaryname, Raddress)) > -1 ) + { + result.push_back(Pair("IsNotary", notaryname)); + IS_KOMODO_NOTARY = 0; + } } NOTARY_PUBKEY = params[0].get_str(); decode_hex(NOTARY_PUBKEY33,33,(char *)NOTARY_PUBKEY.c_str()); USE_EXTERNAL_PUBKEY = 1; + NOTARY_ADDRESS = address.ToString(); } - } else { - result.push_back(Pair("error", "pubkey is wrong length, must be 66 char hex string.")); + else + result.push_back(Pair("error", "pubkey entered is invalid.")); } - } else { - result.push_back(Pair("error", "Can only set pubkey once, to change it you need to restart your daemon, pubkey in use is below.")); - pubkey2addr((char *)Raddress,(uint8_t *)NOTARY_PUBKEY33); - std::string address_ret; address_ret.assign(Raddress); - result.push_back(Pair("address",address_ret)); + else + result.push_back(Pair("error", "pubkey is wrong length, must be 66 char hex string.")); + } + else + { + if ( NOTARY_ADDRESS.empty() ) + { + pubkey2addr((char *)Raddress,(uint8_t *)NOTARY_PUBKEY33); + NOTARY_ADDRESS.assign(Raddress); + } + result.push_back(Pair("error", "Can only set pubkey once, to change it you need to restart your daemon.")); + } + if ( NOTARY_PUBKEY33[0] != 0 && !NOTARY_ADDRESS.empty() ) + { + result.push_back(Pair("address", NOTARY_ADDRESS)); + result.push_back(Pair("pubkey", NOTARY_PUBKEY)); } - result.push_back(Pair("pubkey", NOTARY_PUBKEY)); return result; } UniValue channelsaddress(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::vector destpubkey; CPubKey pk,pk2; char destaddr[64]; + UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::vector pubkey; + cp = CCinit(&C,EVAL_CHANNELS); if ( fHelp || params.size() != 1 ) - throw runtime_error("channelsaddress destpubkey\n"); - if ( ensure_CCrequirements() < 0 ) + throw runtime_error("channelsaddress pubkey\n"); + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - destpubkey = ParseHex(params[0].get_str().c_str()); - pk = pubkey2pk(Mypubkey()); - pk2 = pubkey2pk(destpubkey); - result = CCaddress(cp,(char *)"Channels",destpubkey); - result.push_back(Pair("otherpubkey", params[0].get_str())); - GetCCaddress1of2(cp,destaddr,pk,pk2); - result.push_back(Pair("channeladdress",destaddr)); - if ( 0 ) - { - int32_t i; - for (i=0; i<100; i++) - { - GetCCaddress1of2(cp,destaddr,pk,pk2); - fprintf(stderr,"i.%d %s\n",i,destaddr); - } - } - return(result); + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp,(char *)"Channels",pubkey)); } UniValue cclibaddress(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; std::vector pubkey; - cp = CCinit(&C,EVAL_FIRSTUSER); - if ( fHelp || params.size() > 1 ) - throw runtime_error("cclibaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + struct CCcontract_info *cp,C; std::vector pubkey; uint8_t evalcode = EVAL_FIRSTUSER; + if ( fHelp || params.size() > 2 ) + throw runtime_error("cclibaddress [evalcode] [pubkey]\n"); + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - if ( params.size() == 1 ) - pubkey = ParseHex(params[0].get_str().c_str()); + if ( params.size() >= 1 ) + { + evalcode = atoi(params[0].get_str().c_str()); + if ( evalcode < EVAL_FIRSTUSER || evalcode > EVAL_LASTUSER ) + throw runtime_error("evalcode not between EVAL_FIRSTUSER and EVAL_LASTUSER\n"); + if ( params.size() == 2 ) + pubkey = ParseHex(params[1].get_str().c_str()); + } + cp = CCinit(&C,evalcode); + if ( cp == 0 ) + throw runtime_error("error creating *cp\n"); return(CCaddress(cp,(char *)"CClib",pubkey)); } UniValue cclibinfo(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_FIRSTUSER); + struct CCcontract_info *cp,C; uint8_t evalcode = EVAL_FIRSTUSER; if ( fHelp || params.size() > 0 ) throw runtime_error("cclibinfo\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + cp = CCinit(&C,evalcode); return(CClib_info(cp)); } UniValue cclib(const UniValue& params, bool fHelp) { - struct CCcontract_info *cp,C; char *method; cJSON *jsonparams; - cp = CCinit(&C,EVAL_FIRSTUSER); - if ( fHelp || params.size() > 2 ) - throw runtime_error("cclib method [JSON params]\n"); - if ( ensure_CCrequirements() < 0 ) + struct CCcontract_info *cp,C; char *method,*jsonstr=0; uint8_t evalcode = EVAL_FIRSTUSER; + if ( fHelp || params.size() > 3 ) + throw runtime_error("cclib method [evalcode] [JSON params]\n"); + if ( ASSETCHAINS_CCLIB.size() == 0 ) + throw runtime_error("no -ac_cclib= specified\n"); + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); method = (char *)params[0].get_str().c_str(); - jsonparams = cJSON_Parse(params[1].get_str().c_str()); - return(CClib(cp,method,jsonparams)); + if ( params.size() >= 2 ) + { + evalcode = atoi(params[1].get_str().c_str()); + if ( evalcode < EVAL_FIRSTUSER || evalcode > EVAL_LASTUSER ) + { + //printf("evalcode.%d vs (%d, %d)\n",evalcode,EVAL_FIRSTUSER,EVAL_LASTUSER); + throw runtime_error("evalcode not between EVAL_FIRSTUSER and EVAL_LASTUSER\n"); + } + if ( params.size() == 3 ) + { + jsonstr = (char *)params[2].get_str().c_str(); + //fprintf(stderr,"params.(%s %s %s)\n",params[0].get_str().c_str(),params[1].get_str().c_str(),jsonstr); + } + } + cp = CCinit(&C,evalcode); + return(CClib(cp,method,jsonstr)); +} + +UniValue payments_release(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsrelease \"[%22createtxid%22,amount]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsRelease(cp,(char *)params[0].get_str().c_str())); +} + +UniValue payments_fund(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsfund \"[%22createtxid%22,amount(,useopret)]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsFund(cp,(char *)params[0].get_str().c_str())); +} + +UniValue payments_txidopret(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentstxidopret \"[allocation,%22scriptPubKey%22(,%22destopret%22)]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsTxidopret(cp,(char *)params[0].get_str().c_str())); +} + +UniValue payments_create(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentscreate \"[lockedblocks,minamount,%22paytxid0%22,...,%22paytxidN%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsCreate(cp,(char *)params[0].get_str().c_str())); +} + +UniValue payments_info(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsinfo \"[%22createtxid%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsInfo(cp,(char *)params[0].get_str().c_str())); +} + +UniValue payments_list(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 0 ) + throw runtime_error("paymentslist\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsList(cp,(char *)"")); } UniValue oraclesaddress(const UniValue& params, bool fHelp) @@ -5367,7 +5660,7 @@ UniValue oraclesaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_ORACLES); if ( fHelp || params.size() > 1 ) throw runtime_error("oraclesaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5381,7 +5674,7 @@ UniValue pricesaddress(const UniValue& params, bool fHelp) assetscp = CCinit(&C2,EVAL_PRICES); if ( fHelp || params.size() > 1 ) throw runtime_error("pricesaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5403,7 +5696,7 @@ UniValue pegsaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_PEGS); if ( fHelp || params.size() > 1 ) throw runtime_error("pegssaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5416,7 +5709,7 @@ UniValue marmaraaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_MARMARA); if ( fHelp || params.size() > 1 ) throw runtime_error("Marmaraaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5429,7 +5722,7 @@ UniValue paymentsaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_PAYMENTS); if ( fHelp || params.size() > 1 ) throw runtime_error("paymentsaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5442,7 +5735,7 @@ UniValue gatewaysaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_GATEWAYS); if ( fHelp || params.size() > 1 ) throw runtime_error("gatewaysaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5453,24 +5746,21 @@ UniValue heiraddress(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; std::vector pubkey; cp = CCinit(&C,EVAL_HEIR); - if ( fHelp || params.size() > 1 ) - throw runtime_error("heiraddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - if ( params.size() == 1 ) - pubkey = ParseHex(params[0].get_str().c_str()); + if ( fHelp || params.size() > 1 ) + throw runtime_error("heiraddress pubkey\n"); + if ( ensure_CCrequirements(0) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + pubkey = ParseHex(params[0].get_str().c_str()); return(CCaddress(cp,(char *)"Heir",pubkey)); } - - UniValue lottoaddress(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; std::vector pubkey; cp = CCinit(&C,EVAL_LOTTO); if ( fHelp || params.size() > 1 ) throw runtime_error("lottoaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5483,7 +5773,7 @@ UniValue FSMaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_FSM); if ( fHelp || params.size() > 1 ) throw runtime_error("FSMaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5496,7 +5786,7 @@ UniValue auctionaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_AUCTION); if ( fHelp || params.size() > 1 ) throw runtime_error("auctionaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5509,7 +5799,7 @@ UniValue diceaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_DICE); if ( fHelp || params.size() > 1 ) throw runtime_error("diceaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5519,13 +5809,13 @@ UniValue diceaddress(const UniValue& params, bool fHelp) UniValue faucetaddress(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; std::vector pubkey; - int errno; + int error; cp = CCinit(&C,EVAL_FAUCET); if ( fHelp || params.size() > 1 ) throw runtime_error("faucetaddress [pubkey]\n"); - errno = ensure_CCrequirements(); - if ( errno < 0 ) - throw runtime_error(strprintf("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet. ERR=%d\n", errno)); + error = ensure_CCrequirements(0); + if ( error < 0 ) + throw runtime_error(strprintf("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet. ERR=%d\n", error)); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); return(CCaddress(cp,(char *)"Faucet",pubkey)); @@ -5537,7 +5827,7 @@ UniValue rewardsaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_REWARDS); if ( fHelp || params.size() > 1 ) throw runtime_error("rewardsaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5550,7 +5840,7 @@ UniValue assetsaddress(const UniValue& params, bool fHelp) cp = CCinit(&C, EVAL_ASSETS); if (fHelp || params.size() > 1) throw runtime_error("assetsaddress [pubkey]\n"); - if (ensure_CCrequirements() < 0) + if (ensure_CCrequirements(0) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if (params.size() == 1) pubkey = ParseHex(params[0].get_str().c_str()); @@ -5563,13 +5853,26 @@ UniValue tokenaddress(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_TOKENS); if ( fHelp || params.size() > 1 ) throw runtime_error("tokenaddress [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(0) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); if ( params.size() == 1 ) pubkey = ParseHex(params[0].get_str().c_str()); return(CCaddress(cp,(char *)"Tokens", pubkey)); } +UniValue importgatewayaddress(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; std::vector pubkey; + cp = CCinit(&C,EVAL_IMPORTGATEWAY); + if ( fHelp || params.size() > 1 ) + throw runtime_error("importgatewayddress [pubkey]\n"); + if ( ensure_CCrequirements(0) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + if ( params.size() == 1 ) + pubkey = ParseHex(params[0].get_str().c_str()); + return(CCaddress(cp,(char *)"ImportGateway", pubkey)); +} + UniValue marmara_poolpayout(const UniValue& params, bool fHelp) { int32_t firstheight; double perc; char *jsonstr; @@ -5579,8 +5882,10 @@ UniValue marmara_poolpayout(const UniValue& params, bool fHelp) //marmarapoolpayout 0 2 '[["024131032ed90941e714db8e6dd176fe5a86c9d873d279edecf005c06f773da686",1000]]' throw runtime_error("marmarapoolpayout perc firstheight \"[[\\\"pubkey\\\":shares], ...]\"\n"); } - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); perc = atof(params[0].get_str().c_str()) / 100.; firstheight = atol(params[1].get_str().c_str()); jsonstr = (char *)params[2].get_str().c_str(); @@ -5597,8 +5902,10 @@ UniValue marmara_receive(const UniValue& params, bool fHelp) // after marmarareceive 039433dc3749aece1bd568f374a45da3b0bc6856990d7da3cd175399577940a775 7.5 MARMARA 1168 d72d87aa0d50436de695c93e2bf3d7273c63c92ef6307913aa01a6ee6a16548b throw runtime_error("marmarareceive senderpk amount currency matures batontxid\n"); } - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); memset(&batontxid,0,sizeof(batontxid)); senderpub = ParseHex(params[0].get_str().c_str()); if (senderpub.size()!= 33) @@ -5626,8 +5933,10 @@ UniValue marmara_issue(const UniValue& params, bool fHelp) throw runtime_error("marmaraissue receiverpk amount currency matures approvaltxid\n"); } - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); receiverpub = ParseHex(params[0].get_str().c_str()); if (receiverpub.size()!= 33) { @@ -5649,7 +5958,7 @@ UniValue marmara_transfer(const UniValue& params, bool fHelp) // marmaratransfer 028076d42eb20efc10007fafb5ca66a2052523c0d2221e607adf958d1a332159f6 7.5 MARMARA 1168 1506c774e4b2804a6e25260920840f4cfca8d1fb400e69fe6b74b8e593dbedc5 throw runtime_error("marmaratransfer receiverpk amount currency matures approvaltxid\n"); } - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); receiverpub = ParseHex(params[0].get_str().c_str()); if (receiverpub.size()!= 33) @@ -5657,6 +5966,8 @@ UniValue marmara_transfer(const UniValue& params, bool fHelp) ERR_RESULT("invalid receiverpub pubkey"); return result; } + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); amount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; currency = params[2].get_str(); matures = atol(params[3].get_str().c_str()); @@ -5673,8 +5984,10 @@ UniValue marmara_info(const UniValue& params, bool fHelp) { throw runtime_error("marmarainfo firstheight lastheight minamount maxamount [currency issuerpk]\n"); } - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); firstheight = atol(params[0].get_str().c_str()); lastheight = atol(params[1].get_str().c_str()); minamount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; @@ -5703,8 +6016,10 @@ UniValue marmara_creditloop(const UniValue& params, bool fHelp) // marmaracreditloop 010ff7f9256cefe3b5dee3d72c0eeae9fc6f34884e6f32ffe5b60916df54a9be throw runtime_error("marmaracreditloop txid\n"); } - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); txid = Parseuint256((char *)params[0].get_str().c_str()); result = MarmaraCreditloop(txid); return(result); @@ -5719,8 +6034,10 @@ UniValue marmara_settlement(const UniValue& params, bool fHelp) // marmarasettlement ff3e259869196f3da9b5ea3f9e088a76c4fc063cf36ab586b652e121d441a603 throw runtime_error("marmarasettlement batontxid\n"); } - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_MARMARA) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); batontxid = Parseuint256((char *)params[0].get_str().c_str()); result = MarmaraSettlement(0,batontxid); return(result); @@ -5733,6 +6050,8 @@ UniValue marmara_lock(const UniValue& params, bool fHelp) { throw runtime_error("marmaralock amount unlockht\n"); } + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); amount = atof(params[0].get_str().c_str()) * COIN + 0.00000000499999; if ( params.size() == 2 ) height = atol(params[1].get_str().c_str()); @@ -5744,8 +6063,10 @@ UniValue channelslist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) throw runtime_error("channelsinfo\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); return(ChannelsList()); } @@ -5754,8 +6075,10 @@ UniValue channelsinfo(const UniValue& params, bool fHelp) uint256 opentxid; if ( fHelp || params.size() > 1 ) throw runtime_error("channelsinfo [opentxid]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); opentxid=zeroid; if (params.size() > 0 && !params[0].isNull() && !params[0].get_str().empty()) opentxid = Parseuint256((char *)params[0].get_str().c_str()); @@ -5770,7 +6093,7 @@ UniValue channelsopen(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_CHANNELS); if ( fHelp || params.size() < 3 || params.size() > 4) throw runtime_error("channelsopen destpubkey numpayments payment\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -5797,6 +6120,7 @@ UniValue channelsopen(const UniValue& params, bool fHelp) tokenid=Parseuint256((char *)params[3].get_str().c_str()); } hex = ChannelOpen(0,pubkey2pk(destpub),numpayments,payment,tokenid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5811,7 +6135,7 @@ UniValue channelspayment(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_CHANNELS); if ( fHelp || params.size() < 2 || params.size() >3 ) throw runtime_error("channelspayment opentxid amount [secret]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -5827,6 +6151,7 @@ UniValue channelspayment(const UniValue& params, bool fHelp) secret = Parseuint256((char *)params[2].get_str().c_str()); } hex = ChannelPayment(0,opentxid,amount,secret); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5841,12 +6166,13 @@ UniValue channelsclose(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_CHANNELS); if ( fHelp || params.size() != 1 ) throw runtime_error("channelsclose opentxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); opentxid = Parseuint256((char *)params[0].get_str().c_str()); hex = ChannelClose(0,opentxid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5861,13 +6187,14 @@ UniValue channelsrefund(const UniValue& params, bool fHelp) cp = CCinit(&C,EVAL_CHANNELS); if ( fHelp || params.size() != 2 ) throw runtime_error("channelsrefund opentxid closetxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); opentxid = Parseuint256((char *)params[0].get_str().c_str()); closetxid = Parseuint256((char *)params[1].get_str().c_str()); hex = ChannelRefund(0,opentxid,closetxid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -5881,7 +6208,7 @@ UniValue rewardscreatefunding(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); char *name; int64_t funds,APR,minseconds,maxseconds,mindeposit; std::string hex; if ( fHelp || params.size() > 6 || params.size() < 2 ) throw runtime_error("rewardscreatefunding name amount APR mindays maxdays mindeposit\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_REWARDS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -5950,7 +6277,7 @@ UniValue rewardslock(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; int64_t amount; std::string hex; if ( fHelp || params.size() != 3 ) throw runtime_error("rewardslock name fundingtxid amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_REWARDS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -5980,7 +6307,7 @@ UniValue rewardsaddfunding(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; int64_t amount; std::string hex; if ( fHelp || params.size() != 3 ) throw runtime_error("rewardsaddfunding name fundingtxid amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_REWARDS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6015,7 +6342,7 @@ UniValue rewardsunlock(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string hex; char *name; uint256 fundingtxid,txid; if ( fHelp || params.size() > 3 || params.size() < 2 ) throw runtime_error("rewardsunlock name fundingtxid [txid]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_REWARDS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6043,8 +6370,10 @@ UniValue rewardslist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) throw runtime_error("rewardslist\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_REWARDS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); return(RewardsList()); } @@ -6053,8 +6382,10 @@ UniValue rewardsinfo(const UniValue& params, bool fHelp) uint256 fundingtxid; if ( fHelp || params.size() != 1 ) throw runtime_error("rewardsinfo fundingtxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_REWARDS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); return(RewardsInfo(fundingtxid)); } @@ -6063,28 +6394,75 @@ UniValue gatewayslist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) throw runtime_error("gatewayslist\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); return(GatewaysList()); } +UniValue gatewaysexternaladdress(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; CPubKey pubkey; + + if ( fHelp || params.size() != 2) + throw runtime_error("gatewaysexternaladdress bindtxid pubkey\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + pubkey = ParseHex(params[1].get_str().c_str()); + return(GatewaysExternalAddress(bindtxid,pubkey)); +} + +UniValue gatewaysdumpprivkey(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; + + if ( fHelp || params.size() != 2) + throw runtime_error("gatewaysdumpprivkey bindtxid address\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + LOCK2(cs_main, pwalletMain->cs_wallet); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + std::string strAddress = params[1].get_str(); + CTxDestination dest = DecodeDestination(strAddress); + if (!IsValidDestination(dest)) { + throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid transparent address"); + } + const CKeyID *keyID = boost::get(&dest); + if (!keyID) { + throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to a key"); + } + CKey vchSecret; + if (!pwalletMain->GetKey(*keyID, vchSecret)) { + throw JSONRPCError(RPC_WALLET_ERROR, "Private key for address " + strAddress + " is not known"); + } + return(GatewaysDumpPrivKey(bindtxid,vchSecret)); +} + UniValue gatewaysinfo(const UniValue& params, bool fHelp) { uint256 txid; if ( fHelp || params.size() != 1 ) throw runtime_error("gatewaysinfo bindtxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); txid = Parseuint256((char *)params[0].get_str().c_str()); return(GatewaysInfo(txid)); } UniValue gatewaysbind(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); uint256 tokenid,oracletxid; int32_t i; int64_t totalsupply; std::vector pubkeys; uint8_t M,N; std::string hex,coin; std::vector pubkey; - if ( fHelp || params.size() < 6 ) - throw runtime_error("gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s)\n"); - if ( ensure_CCrequirements() < 0 ) + UniValue result(UniValue::VOBJ); uint256 tokenid,oracletxid; int32_t i; int64_t totalsupply; std::vector pubkeys; + uint8_t M,N,p1,p2,p3,p4=0; std::string hex,coin; std::vector pubkey; + + if ( fHelp || params.size() < 10 ) + throw runtime_error("gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s) pubtype p2shtype wiftype [taddr]\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6096,16 +6474,20 @@ UniValue gatewaysbind(const UniValue& params, bool fHelp) N = atoi((char *)params[5].get_str().c_str()); if ( M > N || N == 0 || N > 15 || totalsupply < COIN/100 || tokenid == zeroid ) throw runtime_error("illegal M or N > 15 or tokensupply or invalid tokenid\n"); + if ( params.size() < 6+N+3 ) + throw runtime_error("not enough parameters for N pubkeys\n"); for (i=0; i 0 ) { @@ -6120,7 +6502,7 @@ UniValue gatewaysdeposit(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int32_t i,claimvout,height; int64_t amount; std::string hex,coin,deposithex; uint256 bindtxid,cointxid; std::vectorproof,destpub,pubkey; if ( fHelp || params.size() != 9 ) throw runtime_error("gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6153,7 +6535,7 @@ UniValue gatewaysclaim(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string hex,coin; uint256 bindtxid,deposittxid; std::vectordestpub; int64_t amount; if ( fHelp || params.size() != 5 ) throw runtime_error("gatewaysclaim bindtxid coin deposittxid destpub amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6179,7 +6561,7 @@ UniValue gatewayswithdraw(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 bindtxid; int64_t amount; std::string hex,coin; std::vector withdrawpub; if ( fHelp || params.size() != 4 ) throw runtime_error("gatewayswithdraw bindtxid coin withdrawpub amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6204,7 +6586,7 @@ UniValue gatewayspartialsign(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string coin,parthex,hex; uint256 txid; if ( fHelp || params.size() != 3 ) throw runtime_error("gatewayspartialsign txidaddr refcoin hex\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6225,7 +6607,7 @@ UniValue gatewayscompletesigning(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 withdrawtxid; std::string txhex,hex,coin; if ( fHelp || params.size() != 3 ) throw runtime_error("gatewayscompletesigning withdrawtxid coin hex\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6247,7 +6629,7 @@ UniValue gatewaysmarkdone(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 completetxid; std::string hex,coin; if ( fHelp || params.size() != 2 ) throw runtime_error("gatewaysmarkdone completesigningtx coin\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6263,13 +6645,29 @@ UniValue gatewaysmarkdone(const UniValue& params, bool fHelp) return(result); } -UniValue gatewayspending(const UniValue& params, bool fHelp) +UniValue gatewayspendingdeposits(const UniValue& params, bool fHelp) { uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) - throw runtime_error("gatewayspending bindtxid coin\n"); - if ( ensure_CCrequirements() < 0 ) + throw runtime_error("gatewayspendingdeposits bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(GatewaysPendingDeposits(bindtxid,coin)); +} + +UniValue gatewayspendingwithdraws(const UniValue& params, bool fHelp) +{ + uint256 bindtxid; std::string coin; + if ( fHelp || params.size() != 2 ) + throw runtime_error("gatewayspendingwithdraws bindtxid coin\n"); + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); bindtxid = Parseuint256((char *)params[0].get_str().c_str()); coin = params[1].get_str(); return(GatewaysPendingWithdraws(bindtxid,coin)); @@ -6280,32 +6678,23 @@ UniValue gatewaysprocessed(const UniValue& params, bool fHelp) uint256 bindtxid; std::string coin; if ( fHelp || params.size() != 2 ) throw runtime_error("gatewaysprocessed bindtxid coin\n"); - if ( ensure_CCrequirements() < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - bindtxid = Parseuint256((char *)params[0].get_str().c_str()); - coin = params[1].get_str(); - return(GatewaysProcessedWithdraws(bindtxid,coin)); -} - -UniValue gatewaysmultisig(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::string hex; char *txidaddr; - if ( fHelp || params.size() != 1 ) - throw runtime_error("gatewaysmultisig txidaddr\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_GATEWAYS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); - txidaddr = (char *)params[0].get_str().c_str(); - return(GatewaysMultisig(txidaddr)); + bindtxid = Parseuint256((char *)params[0].get_str().c_str()); + coin = params[1].get_str(); + return(GatewaysProcessedWithdraws(bindtxid,coin)); } UniValue oracleslist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) throw runtime_error("oracleslist\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); return(OraclesList()); } @@ -6314,8 +6703,10 @@ UniValue oraclesinfo(const UniValue& params, bool fHelp) uint256 txid; if ( fHelp || params.size() != 1 ) throw runtime_error("oraclesinfo oracletxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); txid = Parseuint256((char *)params[0].get_str().c_str()); return(OracleInfo(txid)); } @@ -6325,7 +6716,7 @@ UniValue oraclesregister(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 txid; int64_t datafee; std::string hex; if ( fHelp || params.size() != 2 ) throw runtime_error("oraclesregister oracletxid datafee\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6347,7 +6738,7 @@ UniValue oraclessubscribe(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 txid; int64_t amount; std::string hex; std::vector pubkey; if ( fHelp || params.size() != 3 ) throw runtime_error("oraclessubscribe oracletxid publisher amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6369,8 +6760,10 @@ UniValue oraclessamples(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 txid,batontxid; int32_t num; if ( fHelp || params.size() != 3 ) throw runtime_error("oraclessamples oracletxid batonutxo num\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); txid = Parseuint256((char *)params[0].get_str().c_str()); batontxid = Parseuint256((char *)params[1].get_str().c_str()); num = atoi((char *)params[2].get_str().c_str()); @@ -6382,7 +6775,7 @@ UniValue oraclesdata(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint256 txid; std::vector data; std::string hex; if ( fHelp || params.size() != 2 ) throw runtime_error("oraclesdata oracletxid hexstr\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6403,7 +6796,7 @@ UniValue oraclescreate(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string name,description,format,hex; if ( fHelp || params.size() != 3 ) throw runtime_error("oraclescreate name description format\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6440,7 +6833,7 @@ UniValue FSMcreate(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string name,states,hex; if ( fHelp || params.size() != 2 ) throw runtime_error("FSMcreate name states\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_FSM) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6460,7 +6853,7 @@ UniValue FSMlist(const UniValue& params, bool fHelp) uint256 tokenid; if ( fHelp || params.size() > 0 ) throw runtime_error("FSMlist\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_FSM) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); return(FSMList()); } @@ -6470,7 +6863,7 @@ UniValue FSMinfo(const UniValue& params, bool fHelp) uint256 FSMtxid; if ( fHelp || params.size() != 1 ) throw runtime_error("FSMinfo fundingtxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_FSM) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); FSMtxid = Parseuint256((char *)params[0].get_str().c_str()); return(FSMInfo(FSMtxid)); @@ -6481,7 +6874,7 @@ UniValue faucetinfo(const UniValue& params, bool fHelp) uint256 fundingtxid; if ( fHelp || params.size() != 0 ) throw runtime_error("faucetinfo\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_FAUCET) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); return(FaucetInfo()); } @@ -6491,7 +6884,7 @@ UniValue faucetfund(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t funds; std::string hex; if ( fHelp || params.size() > 1 ) throw runtime_error("faucetfund amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_FAUCET) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6512,7 +6905,7 @@ UniValue faucetget(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string hex; if ( fHelp || params.size() > 0 ) throw runtime_error("faucetget\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_FAUCET) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6528,150 +6921,21 @@ UniValue priceslist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) throw runtime_error("priceslist\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); return(PricesList()); } UniValue pricesinfo(const UniValue& params, bool fHelp) { - uint256 fundingtxid; - if ( fHelp || params.size() != 1 ) + uint256 bettxid; int32_t height; + if ( fHelp || params.size() != 2 ) throw runtime_error("pricesinfo fundingtxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - return(PricesInfo(fundingtxid)); -} - -UniValue pricescreate(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); uint64_t mode; int64_t funding; int32_t i,n,margin,maxleverage; std::string hex; uint256 oracletxid,longtoken,shorttoken,bettoken; std::vector pubkeys; std::vectorpubkey; - if ( fHelp || params.size() < 8 ) - throw runtime_error("pricescreate bettoken oracletxid margin mode longtoken shorttoken maxleverage funding N [pubkeys]\n"); - if ( ensure_CCrequirements() < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - bettoken = Parseuint256((char *)params[0].get_str().c_str()); - oracletxid = Parseuint256((char *)params[1].get_str().c_str()); - margin = atof(params[2].get_str().c_str()) * 1000; - mode = atol(params[3].get_str().c_str()); - longtoken = Parseuint256((char *)params[4].get_str().c_str()); - shorttoken = Parseuint256((char *)params[5].get_str().c_str()); - maxleverage = atol(params[6].get_str().c_str()); - funding = atof(params[7].get_str().c_str()) * COIN + 0.00000000499999; - n = atoi(params[8].get_str().c_str()); - if ( n > 0 ) - { - for (i=0; i 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create prices funding transaction"); - } - return(result); -} - -UniValue pricesaddfunding(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesaddfunding fundingtxid bettoken amount\n"); - if ( ensure_CCrequirements() < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - hex = PricesAddFunding(0,bettoken,fundingtxid,amount); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesaddfunding transaction"); - } - return(result); -} - -UniValue pricesbet(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; int32_t leverage; - if ( fHelp || params.size() != 4 ) - throw runtime_error("pricesbet fundingtxid bettoken amount leverage\n"); - if ( ensure_CCrequirements() < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - leverage = atoi(params[3].get_str().c_str()); - hex = PricesBet(0,bettoken,fundingtxid,amount,leverage); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesbet transaction"); - } - return(result); -} - -UniValue pricesstatus(const UniValue& params, bool fHelp) -{ - uint256 fundingtxid,bettxid,bettoken; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesstatus fundingtxid bettoken bettxid\n"); - if ( ensure_CCrequirements() < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - return(PricesStatus(0,bettoken,fundingtxid,bettxid)); -} - -UniValue pricesfinish(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); uint256 fundingtxid,bettxid,bettoken; std::string hex; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesfinish fundingtxid bettoken bettxid\n"); - if ( ensure_CCrequirements() < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - hex = PricesFinish(0,bettoken,fundingtxid,bettxid); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesfinish transaction"); - } - return(result); + bettxid = Parseuint256((char *)params[0].get_str().c_str()); + height = atoi(params[1].get_str().c_str()); + return(PricesInfo(bettxid,height)); } UniValue dicefund(const UniValue& params, bool fHelp) @@ -6679,7 +6943,7 @@ UniValue dicefund(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t funds,minbet,maxbet,maxodds,timeoutblocks; std::string hex; char *name; if ( fHelp || params.size() != 6 ) throw runtime_error("dicefund name funds minbet maxbet maxodds timeoutblocks\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_DICE) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6712,7 +6976,7 @@ UniValue diceaddfunds(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; int64_t amount; std::string hex; if ( fHelp || params.size() != 3 ) throw runtime_error("diceaddfunds name fundingtxid amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_DICE) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6740,7 +7004,7 @@ UniValue dicebet(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string hex,error; uint256 fundingtxid; int64_t amount,odds; char *name; if ( fHelp || params.size() != 4 ) throw runtime_error("dicebet name fundingtxid amount odds\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_DICE) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6772,7 +7036,7 @@ UniValue dicefinish(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); uint8_t funcid; char *name; uint256 entropyused,fundingtxid,bettxid; std::string hex; int32_t r,entropyvout; if ( fHelp || params.size() != 3 ) throw runtime_error("dicefinish name fundingtxid bettxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_DICE) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6807,7 +7071,7 @@ UniValue dicestatus(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid,bettxid; std::string status,error; double winnings; if ( fHelp || (params.size() != 2 && params.size() != 3) ) throw runtime_error("dicestatus name fundingtxid bettxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_DICE) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -6855,7 +7119,7 @@ UniValue dicelist(const UniValue& params, bool fHelp) { if ( fHelp || params.size() > 0 ) throw runtime_error("dicelist\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_DICE) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); return(DiceList()); } @@ -6865,7 +7129,7 @@ UniValue diceinfo(const UniValue& params, bool fHelp) uint256 fundingtxid; if ( fHelp || params.size() != 1 ) throw runtime_error("diceinfo fundingtxid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_DICE) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); return(DiceInfo(fundingtxid)); @@ -6876,7 +7140,7 @@ UniValue tokenlist(const UniValue& params, bool fHelp) uint256 tokenid; if ( fHelp || params.size() > 0 ) throw runtime_error("tokenlist\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); return(TokenList()); } @@ -6886,8 +7150,10 @@ UniValue tokeninfo(const UniValue& params, bool fHelp) uint256 tokenid; if ( fHelp || params.size() != 1 ) throw runtime_error("tokeninfo tokenid\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); tokenid = Parseuint256((char *)params[0].get_str().c_str()); return(TokenInfo(tokenid)); } @@ -6896,17 +7162,39 @@ UniValue tokenorders(const UniValue& params, bool fHelp) { uint256 tokenid; if ( fHelp || params.size() > 1 ) - throw runtime_error("tokenorders [tokenid]\n"); - if ( ensure_CCrequirements() < 0 ) + throw runtime_error("tokenorders tokenid\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); if (params.size() == 1) { tokenid = Parseuint256((char *)params[0].get_str().c_str()); if (tokenid == zeroid) throw runtime_error("incorrect tokenid\n"); } - else - memset(&tokenid,0,sizeof(tokenid)); - return(AssetOrders(tokenid)); + else { + // memset(&tokenid, 0, sizeof(tokenid)); + throw runtime_error("no tokenid\n"); + } + return AssetOrders(tokenid, CPubKey(), 0); +} + + +UniValue mytokenorders(const UniValue& params, bool fHelp) +{ + uint256 tokenid; + if (fHelp || params.size() > 2) + throw runtime_error("mytokenorders [evalcode]\n"); + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + + uint8_t additionalEvalCode = 0; + if (params.size() == 1) + additionalEvalCode = strtol(params[0].get_str().c_str(), NULL, 0); // supports also 0xEE-like values + + return AssetOrders(zeroid, Mypubkey(), additionalEvalCode); } UniValue tokenbalance(const UniValue& params, bool fHelp) @@ -6916,7 +7204,7 @@ UniValue tokenbalance(const UniValue& params, bool fHelp) if ( fHelp || params.size() > 2 ) throw runtime_error("tokenbalance tokenid [pubkey]\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); LOCK(cs_main); @@ -6949,75 +7237,98 @@ UniValue tokenbalance(const UniValue& params, bool fHelp) UniValue tokencreate(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); std::string name,description,hex; uint64_t supply; - if ( fHelp || params.size() > 3 || params.size() < 2 ) - throw runtime_error("tokencreate name supply description\n"); - if ( ensure_CCrequirements() < 0 ) + UniValue result(UniValue::VOBJ); + std::string name, description, hextx; + std::vector nonfungibleData; + int64_t supply; // changed from uin64_t to int64_t for this 'if ( supply <= 0 )' to work as expected + + CCerror.clear(); + + if ( fHelp || params.size() > 4 || params.size() < 2 ) + throw runtime_error("tokencreate name supply [description][data]\n"); + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); + name = params[0].get_str(); - supply = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; // what for is this '+0.00000000499999'? it will be lost while converting double to int64_t (dimxy) - if ( name.size() == 0 || name.size() > 32) - { + if (name.size() == 0 || name.size() > 32) { ERR_RESULT("Token name must not be empty and up to 32 characters"); return(result); } - if ( supply <= 0 ) - { + + supply = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; // what for is this '+0.00000000499999'? it will be lost while converting double to int64_t (dimxy) + if (supply <= 0) { ERR_RESULT("Token supply must be positive"); return(result); } - if ( params.size() == 3 ) - { + + if (params.size() >= 3) { description = params[2].get_str(); - if ( description.size() > 4096 ) - { + if (description.size() > 4096) { ERR_RESULT("Token description must be <= 4096 characters"); return(result); } } - hex = CreateToken(0,supply,name,description); - if ( hex.size() > 0 ) - { + + if (params.size() == 4) { + nonfungibleData = ParseHex(params[3].get_str()); + if (nonfungibleData.size() > 10000) // opret limit + { + ERR_RESULT("Non-fungible data must be <= 10000"); + return(result); + } + } + + hextx = CreateToken(0, supply, name, description, nonfungibleData); + if( hextx.size() > 0 ) { result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt create transaction"); + result.push_back(Pair("hex", hextx)); + } + else + ERR_RESULT(CCerror); return(result); } UniValue tokentransfer(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); std::string hex; int64_t amount; uint256 tokenid; - if ( fHelp || params.size() != 3 ) + UniValue result(UniValue::VOBJ); + std::string hex; + int64_t amount; + uint256 tokenid; + + CCerror.clear(); + + if ( fHelp || params.size() != 3) throw runtime_error("tokentransfer tokenid destpubkey amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_TOKENS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); + tokenid = Parseuint256((char *)params[0].get_str().c_str()); std::vector pubkey(ParseHex(params[1].get_str().c_str())); //amount = atol(params[2].get_str().c_str()); amount = atoll(params[2].get_str().c_str()); // dimxy changed to prevent loss of significance - if ( tokenid == zeroid ) - { + if( tokenid == zeroid ) { ERR_RESULT("invalid tokenid"); return(result); } - if ( amount <= 0 ) - { + if( amount <= 0 ) { ERR_RESULT("amount must be positive"); return(result); } - hex = TokenTransfer(0,tokenid,pubkey,amount); - if (amount > 0) { - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt transfer assets"); - } else { - ERR_RESULT("amount must be positive"); + + hex = TokenTransfer(0, tokenid, pubkey, amount); + + if( !CCerror.empty() ) { + ERR_RESULT(CCerror); + } + else { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); } return(result); } @@ -7027,7 +7338,7 @@ UniValue tokenconvert(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string hex; int32_t evalcode; int64_t amount; uint256 tokenid; if ( fHelp || params.size() != 4 ) throw runtime_error("tokenconvert evalcode tokenid pubkey amount\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7068,7 +7379,7 @@ UniValue tokenbid(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t bidamount,numtokens; std::string hex; double price; uint256 tokenid; if ( fHelp || params.size() != 3 ) throw runtime_error("tokenbid numtokens tokenid price\n"); - if ( ensure_CCrequirements() < 0 ) + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7110,7 +7421,7 @@ UniValue tokencancelbid(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,bidtxid; if ( fHelp || params.size() != 2 ) throw runtime_error("tokencancelbid tokenid bidtxid\n"); - if ( ensure_CCrequirements() < 0 ) + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7135,7 +7446,7 @@ UniValue tokenfillbid(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t fillamount; std::string hex; uint256 tokenid,bidtxid; if ( fHelp || params.size() != 3 ) throw runtime_error("tokenfillbid tokenid bidtxid fillamount\n"); - if ( ensure_CCrequirements() < 0 ) + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7167,7 +7478,7 @@ UniValue tokenask(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t askamount,numtokens; std::string hex; double price; uint256 tokenid; if ( fHelp || params.size() != 3 ) throw runtime_error("tokenask numtokens tokenid price\n"); - if ( ensure_CCrequirements() < 0 ) + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7201,7 +7512,7 @@ UniValue tokenswapask(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t askamount,numtokens; std::string hex; double price; uint256 tokenid,otherid; if ( fHelp || params.size() != 4 ) throw runtime_error("tokenswapask numtokens tokenid otherid price\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7229,7 +7540,7 @@ UniValue tokencancelask(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,asktxid; if ( fHelp || params.size() != 2 ) throw runtime_error("tokencancelask tokenid asktxid\n"); - if ( ensure_CCrequirements() < 0 ) + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7254,7 +7565,7 @@ UniValue tokenfillask(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t fillunits; std::string hex; uint256 tokenid,asktxid; if ( fHelp || params.size() != 3 ) throw runtime_error("tokenfillask tokenid asktxid fillunits\n"); - if ( ensure_CCrequirements() < 0 ) + if (ensure_CCrequirements(EVAL_ASSETS) < 0 || ensure_CCrequirements(EVAL_TOKENS) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7294,7 +7605,7 @@ UniValue tokenfillswap(const UniValue& params, bool fHelp) UniValue result(UniValue::VOBJ); int64_t fillunits; std::string hex; uint256 tokenid,otherid,asktxid; if ( fHelp || params.size() != 4 ) throw runtime_error("tokenfillswap tokenid otherid asktxid fillunits\n"); - if ( ensure_CCrequirements() < 0 ) + if ( ensure_CCrequirements(EVAL_ASSETS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); @@ -7368,50 +7679,67 @@ UniValue heirfund(const UniValue& params, bool fHelp) int64_t inactivitytime; std::string hex; std::vector pubkey; - std::string name; + std::string name, memo; if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; - if (fHelp || params.size() != 5 && params.size() != 6) - throw runtime_error("heirfund txfee funds heirname heirpubkey inactivitytime [tokenid]\n"); - if (ensure_CCrequirements() < 0) + if (fHelp || params.size() != 6 && params.size() != 7) + throw runtime_error("heirfund txfee funds heirname heirpubkey inactivitytime memo [tokenid]\n"); + if (ensure_CCrequirements(EVAL_HEIR) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); txfee = atoll(params[0].get_str().c_str()); - if (txfee < 0) - throw runtime_error("incorrect txfee param\n"); + if (txfee < 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "incorrect txfee")); + return result; + } - if(params.size() == 6) // tokens in satoshis: + if(params.size() == 7) // tokens in satoshis: amount = atoll(params[1].get_str().c_str()); else // coins: amount = atof(params[1].get_str().c_str()) * COIN; - - if( amount <= 0 ) - throw runtime_error("incorrect amount\n"); + if (amount <= 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "incorrect amount")); + return result; + } name = params[2].get_str(); + pubkey = ParseHex(params[3].get_str().c_str()); - if( !pubkey2pk(pubkey).IsValid() ) - throw runtime_error("incorrect pubkey\n"); + if (!pubkey2pk(pubkey).IsValid()) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "incorrect pubkey")); + return result; + } inactivitytime = atoll(params[4].get_str().c_str()); - if (inactivitytime <= 0) - throw runtime_error("incorrect inactivity time param\n"); + if (inactivitytime <= 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "incorrect inactivity time")); + return result; + } - if (params.size() == 6) { - tokenid = Parseuint256((char*)params[5].get_str().c_str()); - if(tokenid == zeroid) - throw runtime_error("incorrect tokenid\n"); + memo = params[5].get_str(); + + if (params.size() == 7) { + tokenid = Parseuint256((char*)params[6].get_str().c_str()); + if (tokenid == zeroid) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "incorrect tokenid")); + return result; + } } if( tokenid == zeroid ) - result = HeirFundCoinCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, zeroid); + result = HeirFundCoinCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, memo); else - result = HeirFundTokenCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, tokenid); + result = HeirFundTokenCaller(txfee, amount, name, pubkey2pk(pubkey), inactivitytime, memo, tokenid); return result; } @@ -7432,15 +7760,18 @@ UniValue heiradd(const UniValue& params, bool fHelp) if (fHelp || params.size() != 3) throw runtime_error("heiradd txfee funds fundingtxid\n"); - if (ensure_CCrequirements() < 0) + if (ensure_CCrequirements(EVAL_HEIR) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); txfee = atoll(params[0].get_str().c_str()); - if (txfee < 0) - throw runtime_error("incorrect txfee param\n"); + if (txfee < 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "incorrect txfee")); + return result; + } fundingtxid = Parseuint256((char*)params[2].get_str().c_str()); @@ -7450,7 +7781,7 @@ UniValue heiradd(const UniValue& params, bool fHelp) UniValue heirclaim(const UniValue& params, bool fHelp) { - UniValue result; // result(UniValue::VOBJ); + UniValue result; uint256 fundingtxid; int64_t txfee; int64_t inactivitytime; @@ -7458,21 +7789,23 @@ UniValue heirclaim(const UniValue& params, bool fHelp) std::vector pubkey; std::string name; - // do we need this? if (!EnsureWalletIsAvailable(fHelp)) return NullUniValue; if (fHelp || params.size() != 3) throw runtime_error("heirclaim txfee funds fundingtxid\n"); - if (ensure_CCrequirements() < 0) + if (ensure_CCrequirements(EVAL_HEIR) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); txfee = atoll(params[0].get_str().c_str()); - if (txfee < 0) - throw runtime_error("incorrect txfee param\n"); + if (txfee < 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "incorrect txfee")); + return result; + } fundingtxid = Parseuint256((char*)params[2].get_str().c_str()); @@ -7483,9 +7816,10 @@ UniValue heirclaim(const UniValue& params, bool fHelp) UniValue heirinfo(const UniValue& params, bool fHelp) { uint256 fundingtxid; - if (fHelp || params.size() != 1) // or 0? + if (fHelp || params.size() != 1) throw runtime_error("heirinfo fundingtxid\n"); - // if ( ensure_CCrequirements() < 0 ) + + // if ( ensure_CCrequirements(EVAL_HEIR) < 0 ) // throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); fundingtxid = Parseuint256((char*)params[0].get_str().c_str()); @@ -7494,10 +7828,10 @@ UniValue heirinfo(const UniValue& params, bool fHelp) UniValue heirlist(const UniValue& params, bool fHelp) { - if (fHelp || params.size() != 0) // or 0? + if (fHelp || params.size() != 0) throw runtime_error("heirlist\n"); - // if ( ensure_CCrequirements() < 0 ) + // if ( ensure_CCrequirements(EVAL_HEIR) < 0 ) // throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); return (HeirList()); @@ -7606,7 +7940,7 @@ UniValue test_ac(const UniValue& params, bool fHelp) if (fHelp || (params.size() != 4)) throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements() < 0) + if (ensure_CCrequirements(EVAL_HEIR) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); std::vector pubkey1; @@ -7651,7 +7985,7 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp) if (fHelp || (params.size() != 1)) throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements() < 0) + if (ensure_CCrequirements(EVAL_HEIR) < 0) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); @@ -7673,4 +8007,96 @@ UniValue test_heirmarker(const UniValue& params, bool fHelp) cp = CCinit(&C, EVAL_HEIR); return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); -} \ No newline at end of file +} + +UniValue test_burntx(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + + mtx.vin.push_back(CTxIn(tokenid, 0)); + mtx.vin.push_back(CTxIn(tokenid, 1)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); + + std::vector voutPubkeys; + voutPubkeys.push_back(burnpk); + + cp = CCinit(&C, EVAL_TOKENS); + + std::vector vopret; + GetNonfungibleData(tokenid, vopret); + if (vopret.size() > 0) + cp->additionalTokensEvalcode2 = vopret.begin()[0]; + + uint8_t tokenpriv[33]; + char unspendableTokenAddr[64]; + CPubKey unspPk = GetUnspendable(cp, tokenpriv); + GetCCaddress(cp, unspendableTokenAddr, unspPk); + CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); +} + +UniValue test_proof(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + std::vectorproof; + + if (fHelp || (params.size() != 2)) + throw runtime_error("incorrect params\n"); + + + proof = ParseHex(params[0].get_str()); + uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); + + std::vector txids; + + CMerkleBlock merkleBlock; + if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { + result.push_back(Pair("error", "could not unmarshal proof")); + return result; + } + uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); + + result.push_back(Pair("source_root", merkleRoot.GetHex())); + + for (int i = 0; i < txids.size(); i++) + std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; + + + std::vector vMatches(txids.size()); + for (auto v : vMatches) v = true; + CPartialMerkleTree verifTree(txids, vMatches); + + result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); + + if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { + fprintf(stderr, "invalid proof for this cointxid\n"); + } + + std::vector vMerkleTree; + bool f; + ::BuildMerkleTree(&f, txids, vMerkleTree); + + std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); + + uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); + result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); + + return result; +} diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index d171812c4..9b2975db1 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -38,6 +38,7 @@ #include "crypter.h" #include "coins.h" #include "zcash/zip32.h" +#include "cc/CCinclude.h" #include @@ -60,6 +61,9 @@ bool fPayAtLeastCustomFee = true; #include "komodo_defs.h" CBlockIndex *komodo_chainactive(int32_t height); +extern std::string DONATION_PUBKEY; +int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); +int tx_height( const uint256 &hash ); /** * Fees smaller than this (in satoshi) are considered zero fee (for transaction creation) @@ -170,7 +174,7 @@ SaplingPaymentAddress CWallet::GenerateNewSaplingZKey() return addr; } -// Add spending key to keystore +// Add spending key to keystore bool CWallet::AddSaplingZKey( const libzcash::SaplingExtendedSpendingKey &sk, const libzcash::SaplingPaymentAddress &defaultAddr) @@ -180,7 +184,7 @@ bool CWallet::AddSaplingZKey( if (!CCryptoKeyStore::AddSaplingSpendingKey(sk, defaultAddr)) { return false; } - + if (!fFileBacked) { return true; } @@ -189,7 +193,7 @@ bool CWallet::AddSaplingZKey( auto ivk = sk.expsk.full_viewing_key().in_viewing_key(); return CWalletDB(strWalletFile).WriteSaplingZKey(ivk, sk, mapSaplingZKeyMetadata[ivk]); } - + return true; } @@ -571,10 +575,10 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase, return false; } -void CWallet::ChainTip(const CBlockIndex *pindex, +void CWallet::ChainTip(const CBlockIndex *pindex, const CBlock *pblock, SproutMerkleTree sproutTree, - SaplingMerkleTree saplingTree, + SaplingMerkleTree saplingTree, bool added) { if (added) { @@ -1154,7 +1158,7 @@ bool DecrementNoteWitnesses(NoteDataMap& noteDataMap, int indexHeight, int64_t n if (nd->witnesses.size() > 0) { nd->witnesses.pop_front(); } - // indexHeight is the height of the block being removed, so + // indexHeight is the height of the block being removed, so // the new witness cache height is one below it. nd->witnessHeight = indexHeight - 1; } @@ -1404,8 +1408,8 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe return 0; } - bool signSuccess; - SignatureData sigdata; + bool signSuccess; + SignatureData sigdata; uint64_t txfee; auto consensusBranchId = CurrentEpochBranchId(stakeHeight, Params().GetConsensus()); @@ -1744,10 +1748,13 @@ bool CWallet::UpdatedNoteData(const CWalletTx& wtxIn, CWalletTx& wtx) * pblock is optional, but should be provided if the transaction is known to be in a block. * If fUpdate is true, existing transactions will be updated. */ + bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pblock, bool fUpdate) { { AssertLockHeld(cs_wallet); + if ( tx.IsCoinBase() && tx.vout[0].nValue == 0 ) + return false; bool fExisted = mapWallet.count(tx.GetHash()) != 0; if (fExisted && !fUpdate) return false; auto sproutNoteData = FindMySproutNotes(tx); @@ -1759,8 +1766,51 @@ bool CWallet::AddToWalletIfInvolvingMe(const CTransaction& tx, const CBlock* pbl return false; } } + static std::string NotaryAddress; static bool didinit; + if ( !didinit && NotaryAddress.empty() && NOTARY_PUBKEY33[0] != 0 ) + { + didinit = true; + char Raddress[64]; + pubkey2addr((char *)Raddress,(uint8_t *)NOTARY_PUBKEY33); + NotaryAddress.assign(Raddress); + vWhiteListAddress = mapMultiArgs["-whitelistaddress"]; + if ( !vWhiteListAddress.empty() ) + { + fprintf(stderr, "Activated Wallet Filter \n Notary Address: %s \n Adding whitelist address's:\n", NotaryAddress.c_str()); + for ( auto wladdr : vWhiteListAddress ) + fprintf(stderr, " %s\n", wladdr.c_str()); + } + } if (fExisted || IsMine(tx) || IsFromMe(tx) || sproutNoteData.size() > 0 || saplingNoteData.size() > 0) { + // wallet filter for notary nodes. Enables by setting -whitelistaddress= as startup param or in conf file (works same as -addnode byut with R-address's) + if ( !tx.IsCoinBase() && !vWhiteListAddress.empty() && !NotaryAddress.empty() ) + { + int numvinIsOurs = 0, numvinIsWhiteList = 0; + for (size_t i = 0; i < tx.vin.size(); i++) + { + uint256 hash; CTransaction txin; CTxDestination address; + if ( GetTransaction(tx.vin[i].prevout.hash,txin,hash,false) && ExtractDestination(txin.vout[tx.vin[i].prevout.n].scriptPubKey, address) ) + { + if ( CBitcoinAddress(address).ToString() == NotaryAddress ) + numvinIsOurs++; + for ( auto wladdr : vWhiteListAddress ) + { + if ( CBitcoinAddress(address).ToString() == wladdr ) + { + //fprintf(stderr, "We received from whitelisted address.%s\n", wladdr.c_str()); + numvinIsWhiteList++; + } + } + } + } + // Now we know if it was a tx sent to us, by either a whitelisted address, or ourself. + if ( numvinIsOurs != 0 ) + fprintf(stderr, "We sent from address: %s vins: %d\n",NotaryAddress.c_str(),numvinIsOurs); + if ( numvinIsOurs == 0 && numvinIsWhiteList == 0 ) + return false; + } + CWalletTx wtx(this,tx); if (sproutNoteData.size() > 0) { @@ -2177,7 +2227,7 @@ isminetype CWallet::IsMine(const CTransaction& tx, uint32_t voutNum) case TX_SCRIPTHASH: scriptID = CScriptID(uint160(vSolutions[0])); - if (this->GetCScript(scriptID, subscript)) + if (this->GetCScript(scriptID, subscript)) { // if this is a CLTV, handle it differently if (subscript.IsCheckLockTimeVerify()) @@ -2765,6 +2815,8 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate) void CWallet::ReacceptWalletTransactions() { + if ( IsInitialBlockDownload() ) + return; // If transactions aren't being broadcasted, don't let them into local mempool either if (!fBroadcastTransactions) return; @@ -2810,9 +2862,12 @@ void CWallet::ReacceptWalletTransactions() } } } - for (auto hash : vwtxh) + if ( IsInitialBlockDownload() == 0 ) { - EraseFromWallet(hash); + for (auto hash : vwtxh) + { + EraseFromWallet(hash); + } } } @@ -3231,7 +3286,7 @@ void CWallet::AvailableCoins(vector& vCoins, bool fOnlyConfirmed, const int nDepth = pcoin->GetDepthInMainChain(); if (nDepth < 0) continue; - + for (int i = 0; i < pcoin->vout.size(); i++) { isminetype mine = IsMine(pcoin->vout[i]); @@ -4869,7 +4924,7 @@ void CWallet::GetFilteredNotes( } /** - * Find notes in the wallet filtered by payment addresses, min depth, max depth, + * Find notes in the wallet filtered by payment addresses, min depth, max depth, * if the note is spent, if a spending key is required, and if the notes are locked. * These notes are decrypted and added to the output parameter vector, outEntries. */ @@ -4889,11 +4944,21 @@ void CWallet::GetFilteredNotes( CWalletTx wtx = p.second; // Filter the transactions before checking for notes - if (!CheckFinalTx(wtx) || - wtx.GetBlocksToMaturity() > 0 || - wtx.GetDepthInMainChain() < minDepth || - wtx.GetDepthInMainChain() > maxDepth) { + if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0) continue; + + if (minDepth > 1) { + int nHeight = tx_height(wtx.GetHash()); + int nDepth = wtx.GetDepthInMainChain(); + int dpowconfs = komodo_dpowconfs(nHeight,nDepth); + if ( dpowconfs < minDepth || dpowconfs > maxDepth) { + continue; + } + } else { + if ( wtx.GetDepthInMainChain() < minDepth || + wtx.GetDepthInMainChain() > maxDepth) { + continue; + } } for (auto & pair : wtx.mapSproutNoteData) { @@ -5122,10 +5187,10 @@ SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::SaplingE m_wallet->mapSaplingZKeyMetadata[ivk].seedFp = seedFp; } return KeyAdded; - } + } } } -SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { +SpendingKeyAddResult AddSpendingKeyToWallet::operator()(const libzcash::InvalidEncoding& no) const { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid spending key"); } diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h index 663e60367..10aa83ce6 100644 --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -60,6 +60,7 @@ extern bool bSpendZeroConfChange; extern bool fSendFreeTransactions; extern bool fPayAtLeastCustomFee; + //! -paytxfee default static const CAmount DEFAULT_TRANSACTION_FEE = 0; //! -paytxfee will warn if called with a higher fee than this amount (in satoshis) per KB diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index aae406de3..5631fb79e 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -30,6 +30,7 @@ #include "utiltime.h" #include "wallet/wallet.h" #include "zcash/Proof.hpp" +#include "komodo_defs.h" #include #include @@ -486,7 +487,6 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, auto verifier = libzcash::ProofVerifier::Strict(); if (!(CheckTransaction(0,wtx, state, verifier) && (wtx.GetHash() == hash) && state.IsValid())) { - fprintf(stderr, "Removing corrupt tx from wallet.%s\n", hash.ToString().c_str()); deadTxns.push_back(hash); return false; } @@ -878,7 +878,7 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, static bool IsKeyType(string strType) { - return (strType== "key" || strType == "wkey" || + return (strType == "key" || strType == "wkey" || strType == "hdseed" || strType == "chdseed" || strType == "zkey" || strType == "czkey" || strType == "sapzkey" || strType == "csapzkey" || @@ -951,22 +951,35 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) result = DB_CORRUPT; } - if (!deadTxns.empty()) + if ( !deadTxns.empty() ) { - int32_t reAdded = 0; - BOOST_FOREACH (uint256& hash, deadTxns) { - if (!EraseTx(hash)) - fprintf(stderr, "could not delete tx.%s\n",hash.ToString().c_str()); - uint256 blockhash; CTransaction tx; - if (GetTransaction(hash,tx,blockhash,true)) + if ( ASSETCHAINS_STAKED != 0 ) + { + int32_t reAdded = 0; + CWalletDB walletdb(pwallet->strWalletFile, "r+", false); + BOOST_FOREACH (uint256& hash, deadTxns) { - CWalletTx wtx(pwallet,tx); - pwallet->AddToWallet(wtx, true, NULL); - reAdded++; + fprintf(stderr, "Removing corrupt tx from wallet.%s\n", hash.ToString().c_str()); + if (!EraseTx(hash)) + fprintf(stderr, "could not delete tx.%s\n",hash.ToString().c_str()); + uint256 blockhash; CTransaction tx; + if (GetTransaction(hash,tx,blockhash,true)) + { + CWalletTx wtx(pwallet,tx); + pwallet->AddToWallet(wtx, false, &walletdb); + reAdded++; + } } + fprintf(stderr, "Cleared %lu corrupted transactions from wallet. Readded %i known transactions.\n",(long)deadTxns.size(),reAdded); + fNoncriticalErrors = false; + deadTxns.clear(); + } + else if ( (GetBoolArg("-zapwallettxes", false)) ) + { + LogPrintf("Transactions are corrupted. Please restart daemon with -zapwallettxes=2\n"); + fprintf(stderr,"Transactions are corrupted. Please restart daemon with -zapwallettxes=2\n"); + return DB_CORRUPT; } - fprintf(stderr, "Cleared %lu corrupted transactions from wallet. Readded %i known transactions.\n",deadTxns.size(),reAdded); - deadTxns.clear(); } if (fNoncriticalErrors && result == DB_LOAD_OK) @@ -1001,7 +1014,7 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) if (wss.fAnyUnordered) result = ReorderTransactions(pwallet); - + return result; } diff --git a/zcutil/build-mac.sh b/zcutil/build-mac.sh index a3619111f..df4efd05f 100755 --- a/zcutil/build-mac.sh +++ b/zcutil/build-mac.sh @@ -47,14 +47,7 @@ make "$@" -C ./depends/ V=1 NO_QT=1 NO_PROTON=1 WD=$PWD cd src/cc echo $PWD - -if make "$@"; then - echo CCLIB BUILD SUCCESSFUL -else - echo CCLIB BUILD FAILED - exit 1 -fi - +./makerogue cd $WD ./autogen.sh diff --git a/zcutil/build-win.sh b/zcutil/build-win.sh index 5c12893fa..8cf1751af 100755 --- a/zcutil/build-win.sh +++ b/zcutil/build-win.sh @@ -1,5 +1,5 @@ #!/bin/bash -HOST=x86_64-w64-mingw32 +export HOST=x86_64-w64-mingw32 CXX=x86_64-w64-mingw32-g++-posix CC=x86_64-w64-mingw32-gcc-posix PREFIX="$(pwd)/depends/$HOST" @@ -11,6 +11,12 @@ cd "$(dirname "$(readlink -f "$0")")/.." cd depends/ && make HOST=$HOST V=1 NO_QT=1 cd ../ +WD=$PWD +cd src/cc +echo $PWD +./makerogue +cd $WD + ./autogen.sh CONFIG_SITE=$PWD/depends/x86_64-w64-mingw32/share/config.site CXXFLAGS="-DPTW32_STATIC_LIB -DCURL_STATICLIB -DCURVE_ALT_BN128 -fopenmp -pthread" ./configure --prefix="${PREFIX}" --host=x86_64-w64-mingw32 --enable-static --disable-shared sed -i 's/-lboost_system-mt /-lboost_system-mt-s /' configure diff --git a/zcutil/build.sh b/zcutil/build.sh index dc312c8e8..df3dfa234 100755 --- a/zcutil/build.sh +++ b/zcutil/build.sh @@ -42,21 +42,16 @@ if [ "x$*" = 'x--help' ] then cat <