Merge branch 'FSM' into FSM

This commit is contained in:
jl777
2019-04-17 03:05:14 -11:00
committed by GitHub
324 changed files with 88337 additions and 5908 deletions

33
.gitignore vendored
View File

@@ -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

View File

@@ -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}

View File

@@ -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

View File

@@ -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])

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -13,12 +13,14 @@ export BITCOIND=${REAL_BITCOIND}
testScripts=(
'cryptoconditions_faucet.py'
'cryptoconditions_channels.py'
'cryptoconditions_dice.py'
'cryptoconditions_oracles.py'
'cryptoconditions_rewards.py'
'cryptoconditions_token.py'
#'cryptoconditions_gateways.py'
'cryptoconditions_heir.py'
# TODO: cant reconnect nodes back in channels test because of crash (seems regtest only specific)
'cryptoconditions_channels.py'
);
extArg="-extended"

View File

@@ -11,6 +11,8 @@ export BITCOIND=${REAL_BITCOIND}
#Run the tests
testScripts=(
'dpow.py'
'dpowconfs.py'
'ac_private.py'
'verushash.py'
'paymentdisclosure.py'

49
qa/rpc-tests/cryptoconditions.py Executable file → Normal file
View File

@@ -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]

View File

@@ -4,6 +4,7 @@
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import time
from test_framework.test_framework import CryptoconditionsTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import assert_equal, assert_greater_than, \
@@ -25,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)
@@ -70,6 +81,58 @@ 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)
# payments left param should reduce 1 and be equal 9 now ( 10 - 1 = 9 )
result = rpc.channelsinfo(channel_txid)["Transactions"][1]["Payments left"]
assert_equal(result, 9)
# lets try payment with x2 amount to ensure that counters works correct
result = rpc.channelspayment(channel_txid, "200000")
assert_success(result)
payment_tx_id = self.send_and_mine(result["hex"], rpc)
assert payment_tx_id, "got txid"
result = rpc.channelsinfo(channel_txid)
assert_equal(result["Transactions"][2]["Payment"], payment_tx_id)
result = rpc.channelsinfo(channel_txid)["Transactions"][2]["Number of payments"]
assert_equal(result, 2)
result = rpc.channelsinfo(channel_txid)["Transactions"][2]["Payments left"]
assert_equal(result, 7)
# check if payment value really transferred
raw_transaction = rpc.getrawtransaction(payment_tx_id, 1)
result = raw_transaction["vout"][3]["valueSat"]
assert_equal(result, 200000)
result = rpc1.validateaddress(raw_transaction["vout"][3]["scriptPubKey"]["addresses"][0])["ismine"]
assert_equal(result, True)
# have to check that second node have coins to cover txfee at least
rpc.sendtoaddress(rpc1.getnewaddress(), 1)
rpc.sendtoaddress(rpc1.getnewaddress(), 1)
rpc.generate(2)
self.sync_all()
result = rpc1.getbalance()
assert_greater_than(result, 0.1)
# trying to initiate channels payment from node B without any secret
# TODO: have to add RPC validation
payment_hex = rpc1.channelspayment(channel_txid, "100000")
try:
result = rpc1.sendrawtransaction(payment_hex["hex"])
except Exception as e:
pass
# trying to initiate channels payment from node B with secret from previous payment
result = rpc1.channelspayment(channel_txid, "100000", rpc1.channelsinfo(channel_txid)["Transactions"][1]["Secret"])
#result = rpc1.sendrawtransaction(payment_hex["hex"])
assert_error(result)
# executing channel close
result = rpc.channelsclose(channel_txid)
@@ -82,7 +145,7 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework):
# now in channelinfo closed flag should appear
result = rpc.channelsinfo(channel_txid)
assert_equal(result["Transactions"][2]["Close"], channel_close_txid)
assert_equal(result["Transactions"][3]["Close"], channel_close_txid)
# executing channel refund
result = rpc.channelsrefund(channel_txid, channel_close_txid)
@@ -90,6 +153,92 @@ class CryptoconditionsChannelsTest(CryptoconditionsTestFramework):
refund_txid = self.send_and_mine(result["hex"], rpc)
assert refund_txid, "got txid"
# checking if it refunded to opener address
raw_transaction = rpc.getrawtransaction(refund_txid, 1)
result = raw_transaction["vout"][2]["valueSat"]
assert_equal(result, 700000)
result = rpc.validateaddress(raw_transaction["vout"][2]["scriptPubKey"]["addresses"][0])["ismine"]
assert_equal(result, True)
# creating and draining channel (10 payment by 100000 satoshies in total to fit full capacity)
new_channel_hex1 = rpc.channelsopen(self.pubkey1, "10", "100000")
assert_success(new_channel_hex1)
channel1_txid = self.send_and_mine(new_channel_hex1["hex"], rpc)
assert channel1_txid, "got channel txid"
# need to have 2+ confirmations in the test mode
rpc.generate(2)
self.sync_all()
for i in range(10):
result = rpc.channelspayment(channel1_txid, "100000")
assert_success(result)
payment_tx_id = self.send_and_mine(result["hex"], rpc)
assert payment_tx_id, "got txid"
# last payment should indicate that 0 payments left
result = rpc.channelsinfo(channel1_txid)["Transactions"][10]["Payments left"]
assert_equal(result, 0)
# no more payments possible
result = rpc.channelspayment(channel1_txid, "100000")
assert_error(result)
# creating new channel to test the case when node B initiate payment when node A revealed secret in offline
# 10 payments, 100000 sat denomination channel opening with second node pubkey
new_channel_hex2 = rpc.channelsopen(self.pubkey1, "10", "100000")
assert_success(new_channel_hex)
channel2_txid = self.send_and_mine(new_channel_hex2["hex"], rpc)
assert channel2_txid, "got channel txid"
rpc.generate(2)
self.sync_all()
# disconnecting first node from network
rpc.setban("127.0.0.0/24","add")
assert_equal(rpc.getinfo()["connections"], 0)
assert_equal(rpc1.getinfo()["connections"], 0)
rpc1.generate(1)
# sending one payment to mempool to reveal the secret but not mine it
payment_hex = rpc.channelspayment(channel2_txid, "100000")
result = rpc.sendrawtransaction(payment_hex["hex"])
assert result, "got payment txid"
secret = rpc.channelsinfo(channel2_txid)["Transactions"][1]["Secret"]
assert secret, "Secret revealed"
# secret shouldn't be available for node B
secret_not_revealed = None
try:
rpc1.channelsinfo(channel2_txid)["Transactions"][1]["Secret"]
except Exception:
secret_not_revealed = True
assert_equal(secret_not_revealed, True)
# trying to initiate payment from second node with revealed secret
assert_equal(rpc1.getinfo()["connections"], 0)
dc_payment_hex = rpc1.channelspayment(channel2_txid, "100000", secret)
assert_success(dc_payment_hex)
result = rpc1.sendrawtransaction(dc_payment_hex["hex"])
assert result, "got channelspayment transaction id"
# TODO: it crash first node after block generating on mempools merging
# # restoring connection between nodes
# rpc.setban("127.0.0.0/24","remove")
# #rpc.generate(1)
# #rpc1.generate(1)
# sync_blocks(self.nodes)
# rpc.generate(1)
# sync_blocks(self.nodes)
# sync_mempools(self.nodes)
# assert_equal(rpc.getinfo()["connections"], 1)
# assert_equal(rpc1.getinfo()["connections"], 1)
def run_test(self):
print("Mining blocks...")
rpc = self.nodes[0]

View File

@@ -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()

View File

@@ -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()

View File

@@ -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"])

View File

@@ -0,0 +1,176 @@
#!/usr/bin/env python2
# Copyright (c) 2018 SuperNET developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
import time
from test_framework.test_framework import CryptoconditionsTestFramework
from test_framework.authproxy import JSONRPCException
from test_framework.util import assert_equal, assert_greater_than, \
initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \
stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises
from cryptoconditions import assert_success, assert_error, generate_random_string
class CryptoconditionsHeirTest(CryptoconditionsTestFramework):
def run_heir_tests(self):
rpc = self.nodes[0]
rpc1 = self.nodes[1]
result = rpc.heiraddress('')
assert_success(result)
# verify all keys look like valid AC addrs, could be better
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 result.keys():
if x.find('ddress') > 0:
assert_equal(result[x][0], 'R')
# getting empty heir list
result = rpc.heirlist()
assert_equal(result, [])
# valid heirfund case with coins
result = rpc.heirfund("0", "1000", "UNITHEIR", self.pubkey1, "10", "TESTMEMO")
assert_success(result)
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(result, [heir_fund_txid])
# checking heirinfo
result = rpc.heirinfo(heir_fund_txid)
assert_success(result)
assert_equal(result["fundingtxid"], heir_fund_txid)
assert_equal(result["name"], "UNITHEIR")
assert_equal(result["owner"], self.pubkey)
assert_equal(result["heir"], self.pubkey1)
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["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)
rpc.sendtoaddress(rpc1.getnewaddress(), 1)
rpc.generate(2)
self.sync_all()
second_node_balance = rpc1.getbalance()
assert_greater_than(second_node_balance, 0.1)
# let's claim whole heir sum from second node
result = rpc1.heirclaim("0", "1000", heir_fund_txid)
assert_success(result)
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
# + get one block reward when broadcasted heir_claim_txid
result = round(rpc1.getbalance()) - round(second_node_balance)
assert_greater_than(result, 100999)
self.sync_all()
# no more funds should be available for claiming
result = rpc.heirinfo(heir_fund_txid)
assert_equal(result["lifetime"], "1000.00000000")
assert_equal(result["available"], "0.00000000")
# 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...")
rpc = self.nodes[0]
rpc1 = self.nodes[1]
# utxos from block 1 become mature in block 101
if not self.options.noshutdown:
rpc.generate(101)
self.sync_all()
rpc.getinfo()
rpc1.getinfo()
# this corresponds to -pubkey above
print("Importing privkeys")
rpc.importprivkey(self.privkey)
rpc1.importprivkey(self.privkey1)
self.run_heir_tests()
if __name__ == '__main__':
CryptoconditionsHeirTest().main()

View File

@@ -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()
@@ -112,17 +116,15 @@ class CryptoconditionsOraclesTest(CryptoconditionsTestFramework):
# baton
oraclesdata_d = self.send_and_mine(result["hex"], rpc)
result = rpc.oraclessamples(globals()["oracle_{}".format("d")], oraclesdata_d, "1")
# TODO: working not correct now!
#assert_equal("[u'01']", str(result["samples"][0]), "Data match")
assert_equal("[u'01']", str(result["samples"][0]), "Data match")
# D type
result = rpc.oraclesdata(globals()["oracle_{}".format("D")], "0101")
result = rpc.oraclesdata(globals()["oracle_{}".format("D")], "010001")
assert_success(result)
# baton
oraclesdata_D = self.send_and_mine(result["hex"], rpc)
result = rpc.oraclessamples(globals()["oracle_{}".format("D")], oraclesdata_D, "1")
# TODO: working not correct now!
#assert_equal("[u'01']", str(result["samples"][0]), "Data match")
assert_equal("[u'01']", str(result["samples"][0]), "Data match")
# c type
result = rpc.oraclesdata(globals()["oracle_{}".format("c")], "ff")
@@ -198,12 +200,12 @@ class CryptoconditionsOraclesTest(CryptoconditionsTestFramework):
assert_equal("[u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]), "Data match")
# Ihh type
result = rpc.oraclesdata(globals()["oracle_{}".format("Ihh")], "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff")
result = rpc.oraclesdata(globals()["oracle_{}".format("Ihh")], "ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff")
assert_success(result)
# baton
oraclesdata_Ihh = self.send_and_mine(result["hex"], rpc)
result = rpc.oraclessamples(globals()["oracle_{}".format("Ihh")], oraclesdata_Ihh, "1")
assert_equal("[u'0']", str(result["samples"][0]), "Data match")
assert_equal("[u'4294967295', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]), "Data match")
def run_test(self):

View File

@@ -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()

View File

@@ -15,21 +15,39 @@ from cryptoconditions import assert_success, assert_error, generate_random_strin
class CryptoconditionsTokenTest(CryptoconditionsTestFramework):
def run_token_tests(self):
rpc = self.nodes[0]
rpc = self.nodes[0]
rpc1 = self.nodes[1]
result = rpc.tokenaddress()
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.tokenaddress(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')
result = rpc.assetsaddress()
assert_success(result)
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 result.keys():
if x.find('ddress') > 0:
assert_equal(result[x][0], 'R')
# there are no tokens created yet
result = rpc.tokenlist()
assert_equal(result, [])
# trying to create token with negaive supply
# trying to create token with negative supply
result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply")
assert_error(result)
@@ -47,15 +65,12 @@ 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 pubkey
# getting token balance for non existing tokenid
result = rpc.tokenbalance(self.pubkey)
assert_success(result)
assert_equal(result['balance'], 0)
assert_equal(result['CCaddress'], 'RCRsm3VBXz8kKTsYaXKpy7pSEzrtNNQGJC')
assert_equal(result['tokenid'], self.pubkey)
assert_error(result)
# get token balance for token with pubkey
result = rpc.tokenbalance(tokenid, self.pubkey)
@@ -106,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"
@@ -125,17 +140,31 @@ 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
testorder = rpc.tokenask("100", tokenid, "7.77")
testorderid = self.send_and_mine(testorder['hex'], rpc)
# from other node (ensuring that second node have enough balance to cover txfee
# to get the actual error - not "not enough balance" one
rpc.sendtoaddress(rpc1.getnewaddress(), 1)
rpc.sendtoaddress(rpc1.getnewaddress(), 1)
rpc.generate(2)
self.sync_all()
result = rpc1.getbalance()
assert_greater_than(result, 0.1)
result = rpc1.tokencancelask(tokenid, testorderid)
assert_error(result)
# 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, [])
# invalid numtokens bid
result = rpc.tokenbid("-1", tokenid, "1")
assert_error(result)
@@ -159,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"
@@ -178,15 +207,24 @@ 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
testorder = rpc.tokenbid("100", tokenid, "7.77")
testorderid = self.send_and_mine(testorder['hex'], rpc)
# from other node
result = rpc1.getbalance()
assert_greater_than(result, 0.1)
result = rpc1.tokencancelbid(tokenid, testorderid)
assert_error(result)
# 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!)
@@ -220,5 +258,6 @@ class CryptoconditionsTokenTest(CryptoconditionsTestFramework):
rpc1.importprivkey(self.privkey1)
self.run_token_tests()
if __name__ == '__main__':
CryptoconditionsTokenTest().main()

151
qa/rpc-tests/dpowconfs.py Executable file
View File

@@ -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()

View File

@@ -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)

View File

@@ -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,10 +288,12 @@ libbitcoin_server_a_SOURCES = \
bloom.cpp \
cc/eval.cpp \
cc/import.cpp \
cc/importgateway.cpp \
cc/CCassetsCore.cpp \
cc/CCcustom.cpp \
cc/CCtx.cpp \
cc/CCutils.cpp \
cc/CCtokens.cpp \
cc/assets.cpp \
cc/faucet.cpp \
cc/rewards.cpp \
@@ -309,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 \
@@ -323,6 +328,7 @@ libbitcoin_server_a_SOURCES = \
metrics.h \
miner.cpp \
net.cpp \
notaries_staked.cpp \
noui.cpp \
notarisationdb.cpp \
paymentdisclosure.cpp \
@@ -383,6 +389,8 @@ libbitcoin_wallet_a_SOURCES = \
transaction_builder.cpp \
wallet/rpcdisclosure.cpp \
wallet/rpcdump.cpp \
cc/CCtokens.cpp \
cc/CCassetsCore.cpp \
cc/CCassetstx.cpp \
cc/CCtx.cpp \
wallet/rpcwallet.cpp \
@@ -483,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)
@@ -570,6 +580,16 @@ komodod_LDADD += \
$(LIBVERUS_PORTABLE_CRYPTO) \
$(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)
endif

3
src/ac/ilien Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/bash
./komodo-cli -ac_name=ILN $1 $2 $3 $4 $5 $6

2
src/ac/iln Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
./komodo-cli -ac_name=ILN $1 $2 $3 $4 $5 $6

2
src/ac/ksb Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
./komodo-cli -ac_name=KSB $1 $2 $3 $4 $5 $6

2
src/ac/morty Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
./komodo-cli -ac_name=MORTY $1 $2 $3 $4 $5 $6

2
src/ac/our Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
./komodo-cli -ac_name=OUR $1 $2 $3 $4 $5 $6

2
src/ac/rick Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
./komodo-cli -ac_name=RICK $1 $2 $3 $4 $5 $6

2
src/ac/vote2019 Executable file
View File

@@ -0,0 +1,2 @@
#!/bin/bash
./komodo-cli -ac_name=VOTE2019 $1 $2 $3 $4 $5 $6

View File

@@ -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",
@@ -192,5 +189,79 @@
"addnode": [
"51.75.124.34"
]
}
},
{
"ac_name": "KMDICE",
"ac_supply": "10500000",
"ac_reward": "2500000000",
"ac_halving": "210000",
"ac_cc": "2",
"addressindex": "1",
"spentindex": "1",
"addnode": [
"144.76.217.232"
]
},
{
"ac_name": "ZEX",
"ac_founders": "1",
"ac_reward": "13000000000",
"ac_halving": "525600",
"ac_cc": "2",
"ac_pubkey": "039d4a50cc70d1184e462a22edb3b66385da97cc8059196f8305c184a3e21440af",
"addnode": [
"5.9.102.210"
]
},
{
"ac_name": "KSB",
"ac_supply": "1000000000",
"ac_end": "1",
"ac_public": "1",
"addnode": [
"37.187.225.231"
]
},
{
"ac_name": "OUR",
"ac_reward": "1478310502",
"ac_halving": "525600",
"ac_cc": "42",
"ac_supply": "100000000",
"ac_perc": "77700",
"ac_staked": "93",
"ac_pubkey": "02652a3f3e00b3a1875a918314f0bac838d6dd189a346fa623f5efe9541ac0b98c",
"ac_public": "1",
"addnode": [
"51.255.195.65",
"217.182.129.38",
"37.187.225.231"
]
},
{
"ac_name": "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"]
}
]

View File

@@ -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 &
@@ -44,3 +43,10 @@ echo $pubkey
./komodod -pubkey=$pubkey -ac_name=PGT -ac_supply=10000000 -ac_end=1 -addnode=190.114.254.104 &
./komodod -pubkey=$pubkey -ac_name=KMDICE -ac_supply=10500000 -ac_reward=2500000000 -ac_halving=210000 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=144.76.217.232 &
./komodod -pubkey=$pubkey -ac_name=DION -ac_supply=3900000000 -ac_reward=22260000000 -ac_staked=100 -ac_cc=1 -ac_end=4300000000 -addnode=51.75.124.34 &
./komodod -pubkey=$pubkey -ac_name=ZEX -ac_cc=2 -ac_founders=1 -ac_halving=525600 -ac_reward=13000000000 -ac_pubkey=039d4a50cc70d1184e462a22edb3b66385da97cc8059196f8305c184a3e21440af -addnode=5.9.102.210 &
./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 &

View File

@@ -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());

View File

@@ -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 &params) 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<unsigned char> 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;
};

View File

@@ -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();
}
@@ -94,7 +113,8 @@ void WaitForShutdown(boost::thread_group* threadGroup)
//
// Start
//
extern int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,ASSETCHAIN_INIT;
extern int32_t IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY;
extern uint32_t ASSETCHAIN_INIT;
extern std::string NOTARY_PUBKEY;
int32_t komodo_is_issuer();
void komodo_passport_iteration();

View File

@@ -212,7 +212,7 @@ bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction
bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
int64_t AddFaucetInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
int64_t AddFaucetInputs(struct CCcontract_infoCC_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
std::string FaucetGet(uint64_t txfee)

View File

@@ -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<CPubKey> 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<CPubKey> 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::vector<uint8_t>proof,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

View File

@@ -18,12 +18,20 @@
#define CC_HEIR_H
#include "CCinclude.h"
#include "CCtokens.h"
#define EVAL_HEIR 0xea
//#define EVAL_HEIR 0xea
bool HeirValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
// CCcustom
UniValue HeirInfo();
class CoinHelper;
class TokenHelper;
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();
#endif

38
src/cc/CCImportGateway.h Normal file
View File

@@ -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<CPubKey> 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::vector<uint8_t>proof,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

View File

@@ -14,10 +14,30 @@
******************************************************************************/
#ifndef CC_TRIGGERS_H
#define CC_TRIGGERS_H
#ifndef CC_MARMARA_H
#define CC_MARMARA_H
#include "CCinclude.h"
#include "../komodo_cJSON.h"
#define MARMARA_GROUPSIZE 60
#define MARMARA_MINLOCK (1440 * 3 * 30)
#define MARMARA_MAXLOCK (1440 * 24 * 30)
#define MARMARA_VINS 16
#define EVAL_MARMARA 0xef
extern uint8_t ASSETCHAINS_MARMARA;
uint64_t komodo_block_prg(uint32_t nHeight);
int32_t MarmaraGetcreatetxid(uint256 &createtxid,uint256 txid);
int32_t MarmaraGetbatontxid(std::vector<uint256> &creditloop,uint256 &batontxid,uint256 txid);
UniValue MarmaraCreditloop(uint256 txid);
UniValue MarmaraSettlement(uint64_t txfee,uint256 batontxid);
UniValue MarmaraLock(uint64_t txfee,int64_t amount,int32_t height);
UniValue MarmaraPoolPayout(uint64_t txfee,int32_t firstheight,double perc,char *jsonstr); // [[pk0, shares0], [pk1, shares1], ...]
UniValue MarmaraReceive(uint64_t txfee,CPubKey senderpk,int64_t amount,std::string currency,int32_t matures,uint256 batontxid,bool automaticflag);
UniValue MarmaraIssue(uint64_t txfee,uint8_t funcid,CPubKey receiverpk,int64_t amount,std::string currency,int32_t matures,uint256 approvaltxid,uint256 batontxid);
UniValue MarmaraInfo(CPubKey refpk,int32_t firstheight,int32_t lastheight,int64_t minamount,int64_t maxamount,std::string currency);
bool MarmaraValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);

View File

@@ -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

View File

@@ -18,17 +18,33 @@
#define CC_PRICES_H
#include "CCinclude.h"
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<std::string> 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<CPubKey> 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

View File

@@ -29,13 +29,10 @@
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<uint8_t> origpubkey,std::string name,std::string description);
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey);
vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey);
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey);
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx);
int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector<uint8_t> &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid);
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid);
bool ValidateBidRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateAskRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
bool ValidateSwapRemainder(int64_t remaining_price,int64_t remaining_nValue,int64_t orig_nValue,int64_t received_nValue,int64_t paidprice,int64_t totalprice);
@@ -44,18 +41,18 @@ bool SetAskFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValu
bool SetSwapFillamounts(int64_t &paid,int64_t &remaining_price,int64_t orig_nValue,int64_t &received,int64_t totalprice);
int64_t AssetValidateBuyvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid);
int64_t AssetValidateSellvin(struct CCcontract_info *cp,Eval* eval,int64_t &tmpprice,std::vector<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid);
bool AssetExactAmounts(int32_t maxDepth, struct CCcontract_info *cp,int64_t &inputs,int32_t starti,int64_t &outputs,Eval* eval,const CTransaction &tx,uint256 assetid);
//bool AssetExactAmounts(bool doValidateTx, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid, std::vector<CTransaction> &ccVinsTxs);
bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid);
// CCassetstx
int64_t GetAssetBalance(CPubKey pk,uint256 tokenid);
int64_t AddAssetInputs(CMutableTransaction &mtx,CPubKey pk,uint256 assetid,int64_t total,int32_t maxinputs);
UniValue AssetOrders(uint256 tokenid);
UniValue AssetInfo(uint256 tokenid);
UniValue AssetList();
std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
//int64_t GetAssetBalance(CPubKey pk,uint256 tokenid); // --> GetTokenBalance()
int64_t AddAssetInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 assetid, int64_t total, int32_t maxinputs);
UniValue AssetOrders(uint256 tokenid, CPubKey pubkey, uint8_t additionalEvalCode);
//UniValue AssetInfo(uint256 tokenid);
//UniValue AssetList();
//std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description);
//std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total);
//std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode);
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal);
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid);

View File

@@ -1,5 +1,5 @@
/******************************************************************************
* Copyright © 2014-2019 The SuperNET Developers. *
* 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 *
@@ -33,7 +33,7 @@
Yes, this is quite confusing...
In ValudateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or assets, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
In ValidateAssetRemainder the naming convention is nValue is the coin/asset with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or assets, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it.
*/
@@ -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,46 +222,51 @@ 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);
}
/* use EncodeTokenCreateOpRet instead:
CScript EncodeAssetCreateOpRet(uint8_t funcid,std::vector<uint8_t> origpubkey,std::string name,std::string description)
{
CScript opret; uint8_t evalcode = EVAL_ASSETS;
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << origpubkey << name << description);
return(opret);
}
*/
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey)
vscript_t EncodeAssetOpRet(uint8_t assetFuncId, uint256 assetid2, int64_t price, std::vector<uint8_t> origpubkey)
{
CScript opret; uint8_t evalcode = EVAL_ASSETS;
assetid = revuint256(assetid);
switch ( funcid )
vscript_t vopret;
uint8_t evalcode = EVAL_ASSETS;
switch ( assetFuncId )
{
case 't': case 'x': case 'o':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid);
//case 't': this cannot be here
case 'x': case 'o':
vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId);
break;
case 's': case 'b': case 'S': case 'B':
opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << assetid << 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 << funcid << assetid << assetid2 << price << origpubkey);
vopret = /*<< OP_RETURN <<*/ E_MARSHAL(ss << evalcode << assetFuncId << assetid2 << price << origpubkey);
break;
default:
fprintf(stderr,"EncodeOpRet: illegal funcid.%02x\n",funcid);
opret << OP_RETURN;
fprintf(stderr,"EncodeAssetOpRet: illegal funcid.%02x\n", assetFuncId);
//opret << OP_RETURN;
break;
}
return(opret);
return(vopret);
}
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description)
/* it is for compatibility, do not use this for new contracts (use DecodeTokenCreateOpRet)
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description)
{
std::vector<uint8_t> vopret; uint8_t evalcode,funcid,*script;
GetOpReturnData(scriptPubKey, vopret);
@@ -272,190 +277,216 @@ bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &or
return(true);
}
return(0);
}
} */
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey)
uint8_t DecodeAssetTokenOpRet(const CScript &scriptPubKey, uint8_t &assetsEvalCode, uint256 &tokenid, uint256 &assetid2, int64_t &price, std::vector<uint8_t> &origpubkey)
{
std::vector<uint8_t> vopret; uint8_t funcid=0,*script,e,f;
GetOpReturnData(scriptPubKey, vopret);
script = (uint8_t *)vopret.data();
memset(&assetid,0,sizeof(assetid));
memset(&assetid2,0,sizeof(assetid2));
price = 0;
if ( script != 0 && script[0] == EVAL_ASSETS )
{
funcid = script[1];
//fprintf(stderr,"decode.[%c]\n",funcid);
switch ( funcid )
vscript_t vopretAssets; //, vopretAssetsStripped;
uint8_t *script, funcId = 0, assetsFuncId = 0, dummyEvalCode, dummyAssetFuncId;
uint256 dummyTokenid;
std::vector<CPubKey> voutPubkeysDummy;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
tokenid = zeroid;
assetid2 = zeroid;
price = 0;
assetsEvalCode = 0;
assetsFuncId = 0;
// First - decode token opret:
funcId = DecodeTokenOpRet(scriptPubKey, dummyEvalCode, tokenid, voutPubkeysDummy, oprets);
GetOpretBlob(oprets, OPRETID_ASSETSDATA, vopretAssets);
LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() from DecodeTokenOpRet returned funcId=" << (int)funcId << std::endl);
if (funcId == 0 || vopretAssets.size() < 2) {
LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() incorrect opret or no asset's payload" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << std::endl);
return (uint8_t)0;
}
//if (!E_UNMARSHAL(vopretAssets, { ss >> vopretAssetsStripped; })) { //strip string size
// std::cerr << "DecodeAssetTokenOpRet() could not unmarshal vopretAssetsStripped" << std::endl;
// return (uint8_t)0;
//}
// additional check to prevent crash
if (vopretAssets.size() >= 2) {
assetsEvalCode = vopretAssets.begin()[0];
assetsFuncId = vopretAssets.begin()[1];
LOGSTREAM((char *)"ccassets", CCLOG_DEBUG2, stream << "DecodeAssetTokenOpRet() assetsEvalCode=" << (int)assetsEvalCode << " funcId=" << (char)(funcId ? funcId : ' ') << " assetsFuncId=" << (char)(assetsFuncId ? assetsFuncId : ' ') << std::endl);
if (assetsEvalCode == EVAL_ASSETS)
{
case 'c': return(funcid);
break;
case 't': case 'x': case 'o':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid) != 0 )
//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
{
assetid = revuint256(assetid);
return(funcid);
return(assetsFuncId);
}
break;
case 's': case 'b': case 'S': case 'B':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> price; ss >> origpubkey) != 0 )
if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> price; ss >> origpubkey) != 0)
{
assetid = revuint256(assetid);
//fprintf(stderr,"got price %llu\n",(long long)price);
return(funcid);
//fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price);
return(assetsFuncId);
}
break;
case 'E': case 'e':
if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> assetid; ss >> assetid2; ss >> price; ss >> origpubkey) != 0 )
if (E_UNMARSHAL(vopretAssets, ss >> dummyEvalCode; ss >> dummyAssetFuncId; ss >> assetid2; ss >> price; ss >> origpubkey) != 0)
{
//fprintf(stderr,"got price %llu\n",(long long)price);
assetid = revuint256(assetid);
//fprintf(stderr,"DecodeAssetTokenOpRet() got price %llu\n",(long long)price);
assetid2 = revuint256(assetid2);
return(funcid);
return(assetsFuncId);
}
break;
default:
fprintf(stderr,"DecodeAssetOpRet: illegal funcid.%02x\n",funcid);
funcid = 0;
break;
}
}
}
return(funcid);
LOGSTREAM((char *)"ccassets", CCLOG_INFO, stream << "DecodeAssetTokenOpRet() no asset's payload or incorrect assets funcId or evalcode" << " funcId=" << (int)funcId << " vopretAssets.size()=" << vopretAssets.size() << " assetsEvalCode=" << assetsEvalCode << " assetsFuncId=" << assetsFuncId << std::endl);
return (uint8_t)0;
}
// extract sell/buy owner's pubkey from the opret
bool SetAssetOrigpubkey(std::vector<uint8_t> &origpubkey,int64_t &price,const CTransaction &tx)
{
uint256 assetid,assetid2;
if ( tx.vout.size() > 0 && DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 )
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);
else
return(false);
}
bool GetAssetorigaddrs(struct CCcontract_info *cp,char *CCaddr,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; std::vector<uint8_t> origpubkey; CScript script;
n = tx.vout.size();
if ( n == 0 || (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
uint256 assetid, assetid2;
int64_t price,nValue=0;
int32_t n;
uint8_t vintxFuncId;
std::vector<uint8_t> origpubkey;
CScript script;
uint8_t evalCode;
n = vintx.vout.size();
if( n == 0 || (vintxFuncId = DecodeAssetTokenOpRet(vintx.vout[n-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) == 0 )
return(false);
if ( GetCCaddress(cp,CCaddr,pubkey2pk(origpubkey)) != 0 && Getscriptaddress(destaddr,CScript() << origpubkey << OP_CHECKSIG) != 0 )
bool bGetCCaddr = false;
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
if (vintxFuncId == 's' || vintxFuncId == 'S') {
// bGetCCaddr = GetCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey));
cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present
bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible
}
else if (vintxFuncId == 'b' || vintxFuncId == 'B') {
cpTokens->additionalTokensEvalcode2 = cp->additionalTokensEvalcode2; // add non-fungible if present
bGetCCaddr = GetTokensCCaddress(cpTokens, origCCaddr, pubkey2pk(origpubkey)); // tokens to single-eval token or token+nonfungible
}
else {
std::cerr << "GetAssetorigaddrs incorrect vintx funcid=" << (char)(vintxFuncId?vintxFuncId:' ') << std::endl;
return false;
}
if( bGetCCaddr && Getscriptaddress(origNormalAddr, CScript() << origpubkey << OP_CHECKSIG))
return(true);
else return(false);
else
return(false);
}
// Checks if the vout is a really Asset CC vout
// if maxAssetExactAmountDepth > 0, it also validates the vin transaction itself:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
int64_t IsAssetvout(int32_t maxAssetExactAmountDepth, struct CCcontract_info *cp, Eval* eval, int64_t &price,std::vector<uint8_t> &origpubkey,const CTransaction& tx,int32_t v,uint256 refassetid)
int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *origCCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
{
uint256 assetid,assetid2; int64_t nValue=0; int32_t n; uint8_t funcid;
uint256 hashBlock;
uint256 assetid, assetid2;
int64_t tmpprice;
std::vector<uint8_t> tmporigpubkey;
uint8_t evalCode;
if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) // maybe check address too?
{
char destaddr[64], unspendableAddr[64];
if (maxAssetExactAmountDepth > 0) {
//validate all tx
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
std::vector<CTransaction> ccVinsTxs;
//std::cerr << "IsAssetvout() validate=yes" << std::endl;
const bool validateVinTxs = false;
bool isEqualAmounts = AssetExactAmounts(maxAssetExactAmountDepth, cp, myCCVinsAmount, 0, myCCVoutsAmount, eval, tx, refassetid);
// if ccInputs != ccOutputs and it is not the tokenbase tx means it is possibly fake tx (dimxy):
if (!isEqualAmounts && refassetid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
std::cerr << "IsAssetvout() detected bad tx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx" << std::endl;
return 0;
}
}
origaddr[0] = destaddr[0] = origCCaddr[0] = 0;
uint8_t funcid = 0;
if (tx.vout.size() > 0) {
uint256 assetid, assetid2;
int64_t tmpprice;
std::vector<uint8_t> tmporigpubkey;
uint8_t evalCode;
n = tx.vout.size();
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
nValue = tx.vout[v].nValue;
funcid = DecodeAssetTokenOpRet(tx.vout[tx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey);
}
// fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f\n",v,n,(double)nValue/COIN);
if ( (funcid= DecodeAssetOpRet(tx.vout[n-1].scriptPubKey,assetid,assetid2,price,origpubkey)) == 0 )
{
fprintf(stderr,"IsAssetvout() null decodeopret v.%d\n",v);
return(0);
}
else if ( funcid == 'c' )
{
if (refassetid == tx.GetHash() && v == 0) {
std::cerr << "isAssetVout() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning nValue=" << nValue << std::endl;
return(nValue);
}
}
else if ( (funcid == 'b' || funcid == 'B') && v == 0 ) // critical! 'b'/'B' vout0 is NOT asset
return(0);
else if ( funcid != 'E' )
{
if ( assetid == refassetid )
{
fprintf(stderr,"IsAssetvout() returning %.8f\n",(double)nValue/COIN);
return(nValue);
}
}
else if ( funcid == 'E' )
{
if ( v < 2 && assetid == refassetid )
return(nValue);
else if ( v == 2 && assetid2 == refassetid )
return(nValue);
}
}
//fprintf(stderr,"Isassetvout: normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
int64_t AssetValidateCCvin(struct CCcontract_info *cp,Eval* eval,char *CCaddr,char *origaddr,const CTransaction &tx,int32_t vini,CTransaction &vinTx)
{
uint256 hashBlock; char destaddr[64];
origaddr[0] = destaddr[0] = CCaddr[0] = 0;
if ( tx.vin.size() < 2 )
if( tx.vin.size() < 2 )
return eval->Invalid("not enough for CC vins");
else if ( tx.vin[vini].prevout.n != 0 )
else if( tx.vin[vini].prevout.n != 0 )
return eval->Invalid("vin1 needs to be buyvin.vout[0]");
else if ( eval->GetTxUnconfirmed(tx.vin[vini].prevout.hash,vinTx,hashBlock) == 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;
return eval->Invalid("always should find CCvin, but didnt");
}
else if ( Getscriptaddress(destaddr,vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 || strcmp(destaddr,(char *)cp->unspendableCCaddr) != 0 )
// 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,"%s vs %s\n",destaddr,(char *)cp->unspendableCCaddr);
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 --> should spend coins from asset unspendable addr
else if ((funcid == 'B' || funcid == 'o') &&
(Getscriptaddress(destaddr, vinTx.vout[tx.vin[vini].prevout.n].scriptPubKey) == 0 ||
!GetCCaddress(cp, unspendableAddr, GetUnspendable(cp, NULL)) ||
strcmp(destaddr, unspendableAddr) != 0))
{
fprintf(stderr, "AssetValidateCCvin() cc addr %s is not evalcode=0x%02x asset unspendable addr %s\n", destaddr, (int)cp->evalcode, unspendableAddr);
return eval->Invalid("invalid vin assets CCaddr");
}
// end of check source unspendable cc address
//else if ( vinTx.vout[0].nValue < 10000 )
// return eval->Invalid("invalid dust for buyvin");
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,"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<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 refassetid)
{
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid;
CTransaction vinTx; int64_t nValue; uint256 assetid,assetid2; uint8_t funcid, evalCode;
CCaddr[0] = origaddr[0] = 0;
if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
return(0);
// validate locked coins on Assets vin[1]
if ( (nValue= AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 )
return(0);
else if ( vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout0 for buyvin");
else if ((funcid = DecodeAssetTokenOpRet(vinTx.vout[vinTx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, tmpprice, tmporigpubkey)) == 'b' &&
vinTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) // marker is only in 'b'?
return eval->Invalid("invalid normal vout1 for buyvin");
else
{
//fprintf(stderr,"have %.8f checking assetid origaddr.(%s)\n",(double)nValue/COIN,origaddr);
if ( vinTx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,assetid,assetid2,tmpprice,tmporigpubkey)) != 'b' && funcid != 'B' )
if ( vinTx.vout.size() > 0 && funcid != 'b' && funcid != 'B' )
return eval->Invalid("invalid opreturn for buyvin");
else if ( refassetid != assetid )
return eval->Invalid("invalid assetid for buyvin");
@@ -469,88 +500,151 @@ 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<uint8_t> &tmporigpubkey,char *CCaddr,char *origaddr,const CTransaction &tx,uint256 assetid)
{
CTransaction vinTx; int64_t nValue,assetoshis;
fprintf(stderr,"AssetValidateSellvin\n");
if ( (nValue= AssetValidateCCvin(cp,eval,CCaddr,origaddr,tx,1,vinTx)) == 0 )
//fprintf(stderr,"AssetValidateSellvin()\n");
if ( (nValue = AssetValidateCCvin(cp, eval, CCaddr, origaddr, tx, 1, vinTx)) == 0 )
return(0);
if ( (assetoshis= IsAssetvout(1, cp, NULL, tmpprice,tmporigpubkey,vinTx,0,assetid)) == 0 )
if ( (assetoshis= IsAssetvout(cp, tmpprice, tmporigpubkey, vinTx, 0, assetid)) == 0 )
return eval->Invalid("invalid missing CC vout0 for sellvin");
else return(assetoshis);
else
return(assetoshis);
}
// overload with additional params for deep tx validation (dimxy)
bool AssetExactAmounts(int maxDepth, struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid)
// validates opret for asset tx:
bool ValidateAssetOpret(CTransaction tx, int32_t v, uint256 assetid, int64_t &price, std::vector<uint8_t> &origpubkey) {
uint256 assetidOpret, assetidOpret2;
uint8_t funcid, evalCode;
// this is just for log messages indentation fur debugging recursive calls:
int32_t n = tx.vout.size();
if ((funcid = DecodeAssetTokenOpRet(tx.vout[n - 1].scriptPubKey, evalCode, assetidOpret, assetidOpret2, price, origpubkey)) == 0)
{
std::cerr << "ValidateAssetOpret() DecodeAssetTokenOpRet returned funcId=0 for opret from txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
/* it is now on token level:
else if (funcid == 'c')
{
if (assetid != zeroid && assetid == tx.GetHash() && v == 0) {
//std::cerr << "ValidateAssetOpret() this is the tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
}
else if (funcid == 't') // TODO: check if this new block does not influence IsAssetVout
{
//std::cerr << "ValidateAssetOpret() assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
if (assetid != zeroid && assetid == assetidOpret) {
//std::cerr << "ValidateAssetOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " vout=" << v << " returning true" << std::endl;
return(true);
}
} */
//else if ((funcid == 'b' || funcid == 'B') && v == 0) // critical! 'b'/'B' vout0 is NOT asset
// return(false);
else if (funcid != 'E')
{
if (assetid != zeroid && assetidOpret == assetid)
{
//std::cerr << "ValidateAssetOpret() returns true for not 'E', funcid=" << (char)funcid << std::endl;
return(true);
}
}
else if (funcid == 'E') // NOTE: not implemented yet!
{
if (v < 2 && assetid != zeroid && assetidOpret == assetid)
return(true);
else if (v == 2 && assetid != zeroid && assetidOpret2 == assetid)
return(true);
}
//std::cerr << "ValidateAssetOpret() return false funcid=" << (char)funcid << " assetid=" << assetid.GetHex() << " assetIdOpret=" << assetidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
return false;
}
// Checks if the vout is a really Asset CC vout
int64_t IsAssetvout(struct CCcontract_info *cp, int64_t &price, std::vector<uint8_t> &origpubkey, const CTransaction& tx, int32_t v, uint256 refassetid)
{
CTransaction vinTx; uint256 hashBlock,id,id2; int32_t i,flag,numvins,numvouts; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
numvins = tx.vin.size();
numvouts = tx.vout.size();
inputs = outputs = 0;
maxDepth--;
//std::cerr << "IsAssetvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for assetid=" << refassetid.GetHex() << std::endl;
for (i=starti; i<numvins; i++)
{
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
{
//std::cerr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are really not inside validation! -- dimxy
if ( (eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)) )
{
fprintf(stderr,"AssetExactAmounts() cannot read vintx i.%d starti.%d numvins.%d\n", i,starti,numvins);
return (!eval) ? false : eval->Invalid("always should find vin, but didnt");
} // false means 'don't go deeper' -- dimxy
else if ( (assetoshis= IsAssetvout( maxDepth, cp, eval, tmpprice,tmporigpubkey,vinTx,tx.vin[i].prevout.n,assetid)) != 0 )
{
fprintf(stderr,"AssetExactAmounts() vin%d %llu, ",i,(long long)assetoshis);
inputs += assetoshis;
}
else
{
if ( vinTx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 && DecodeAssetOpRet(vinTx.vout[vinTx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid )
{
assetoshis = vinTx.vout[i].nValue;
fprintf(stderr,"AssetExactAmounts() vin%d assetoshis=%llu special case, ",i,(long long)assetoshis);
inputs += assetoshis;
}
}
}
int32_t n = tx.vout.size();
// just check boundaries:
if (v >= n - 1) { // just moved this up (dimxy)
std::cerr << "isAssetVout() internal err: (v >= n - 1), returning 0" << std::endl;
return(0);
}
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0) // maybe check address too? dimxy: possibly no, because there are too many cases with different addresses here
{
// moved opret checking to this new reusable func (dimxy):
const bool valOpret = ValidateAssetOpret(tx, v, refassetid, price, origpubkey);
//std::cerr << "IsAssetvout() ValidateAssetOpret returned=" << std::boolalpha << valOpret << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
if (valOpret) {
//std::cerr << "IsAssetvout() ValidateAssetOpret returned true, returning nValue=" << tx.vout[v].nValue << " for txid=" << tx.GetHash().GetHex() << " for assetid=" << refassetid.GetHex() << std::endl;
return tx.vout[v].nValue;
}
if ( DecodeAssetOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,id,id2,tmpprice,tmporigpubkey) == 't' && id == assetid )
flag = 1;
else flag = 0;
//fprintf(stderr,"IsAssetvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
}
//fprintf(stderr,"IsAssetvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
for (i=0; i<numvouts; i++)
{ // 'false' means 'dont go deep' -- dimxy
if ( (assetoshis= IsAssetvout(maxDepth, cp, eval, tmpprice,tmporigpubkey,tx,i,assetid)) != 0 )
{
fprintf(stderr,"AssetExactAmounts() vout%d assetoshis=%llu, ",i,(long long)assetoshis);
outputs += assetoshis;
}
// Note: account it only if this is 'transfer' tx -- dimxy
else if ( flag != 0 && tx.vout[i].scriptPubKey.IsPayToCryptoCondition() != 0 )
{
assetoshis = tx.vout[i].nValue;
fprintf(stderr,"AssetExactAmounts() vout%d assetoshis=%llu special case, ",i,(long long)assetoshis);
outputs += assetoshis;
}
}
// sets cc inputs vs cc outputs and ensures they are equal:
bool AssetCalcAmounts(struct CCcontract_info *cpAssets, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid)
{
CTransaction vinTx; uint256 hashBlock, id, id2; int32_t flag; int64_t assetoshis; std::vector<uint8_t> tmporigpubkey; int64_t tmpprice;
int32_t numvins = tx.vin.size();
int32_t numvouts = tx.vout.size();
inputs = outputs = 0;
//std::cerr << "AssetExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
struct CCcontract_info *cpTokens, C;
if ( inputs != outputs )
{
std::cerr << "AssetExactAmounts() incorrect inputs=" << (double)inputs / COIN << " vs outputs=" << (double)outputs/COIN << " for txid=" << tx.GetHash().GetHex() << std::endl;
return(false);
}
else return(true);
cpTokens = CCinit(&C, EVAL_TOKENS);
for (int32_t i = 0; i<numvins; i++)
{ // only tokens are relevant!!
if (/*(*cpAssets->ismyvin)(tx.vin[i].scriptSig)*/ (*cpTokens->ismyvin)(tx.vin[i].scriptSig) ) // || IsVinAllowed(tx.vin[i].scriptSig) != 0)
{
//std::cerr << indentStr << "AssetExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are not inside the validation code -- dimxy
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
{
std::cerr << "AssetCalcAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl;
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
}
else {
// validate vouts of vintx
//std::cerr << indentStr << "AssetExactAmounts() check vin i=" << i << " nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl;
//assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, vinTx, tx.vin[i].prevout.n, assetid);
std::vector<uint8_t> vopretExtra;
std::vector<CPubKey> vinPubkeysEmpty;
// TODO: maybe we do not need call to IsTokensVout here, cause we've already selected token vins
assetoshis = IsTokensvout(false, false, cpTokens, NULL, vinTx, tx.vin[i].prevout.n, assetid);
if (assetoshis != 0)
{
//std::cerr << "AssetCalcAmounts() vin i=" << i << " assetoshis=" << assetoshis << std::endl;
inputs += assetoshis;
}
}
}
}
for (int32_t i = 0; i < numvouts-1; i++)
{
assetoshis = IsAssetvout(cpAssets, tmpprice, tmporigpubkey, tx, i, assetid);
if (assetoshis != 0)
{
//std::cerr << "AssetCalcAmounts() vout i=" << i << " assetoshis=" << assetoshis << std::endl;
outputs += assetoshis;
}
}
//std::cerr << "AssetCalcAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
/* we do not verify inputs == outputs here,
it's now done in Tokens */
return(true);
}
// overload for existing calls of this function (dimxy)
/*bool AssetExactAmounts(struct CCcontract_info *cp, int64_t &inputs, int32_t starti, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 assetid) {
std::vector<CTransaction> ccVinsTxs;
return AssetExactAmounts(true, cp, inputs, starti, outputs, eval, tx, assetid);
}*/

View File

@@ -14,181 +14,150 @@
******************************************************************************/
#include "CCassets.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<uint8_t> origpubkey; CTransaction vintx; int32_t j,vout,n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
threshold = total/(maxinputs!=0?maxinputs:64);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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; j<mtx.vin.size(); j++)
if ( txid == mtx.vin[j].prevout.hash && vout == mtx.vin[j].prevout.n )
break;
if ( j != mtx.vin.size() )
continue;
if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
{
Getscriptaddress(destaddr,vintx.vout[vout].scriptPubKey);
if ( strcmp(destaddr,coinaddr) != 0 && strcmp(destaddr,cp->unspendableCCaddr) != 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(1, cp, NULL, 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);
}
int64_t GetAssetBalance(CPubKey pk,uint256 tokenid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
return(AddAssetInputs(cp,mtx,pk,tokenid,0,0));
}
UniValue AssetInfo(uint256 assetid)
{
UniValue result(UniValue::VOBJ); uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description; char str[67],numstr[65];
if ( GetTransaction(assetid,vintx,hashBlock,false) == 0 )
{
fprintf(stderr,"cant find assetid\n");
result.push_back(Pair("result","error"));
result.push_back(Pair("error","cant find assetid"));
return(result);
}
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) == 0 )
{
fprintf(stderr,"assetid isnt assetcreation txid\n");
result.push_back(Pair("result","error"));
result.push_back(Pair("error","assetid isnt assetcreation txid"));
}
result.push_back(Pair("result","success"));
result.push_back(Pair("tokenid",uint256_str(str,assetid)));
result.push_back(Pair("owner",pubkey33_str(str,origpubkey.data())));
result.push_back(Pair("name",name));
result.push_back(Pair("supply",vintx.vout[0].nValue));
result.push_back(Pair("description",description));
return(result);
}
UniValue AssetList()
{
UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description; char str[65];
cp = CCinit(&C,EVAL_ASSETS);
SetCCtxids(addressIndex,cp->normaladdr);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) != 0 )
{
result.push_back(uint256_str(str,txid));
}
}
}
return(result);
}
UniValue AssetOrders(uint256 refassetid)
UniValue AssetOrders(uint256 refassetid, CPubKey pk, uint8_t additionalEvalCode)
{
static uint256 zero;
int64_t price; uint256 txid,hashBlock,assetid,assetid2; std::vector<uint8_t> origpubkey; CTransaction vintx; UniValue result(UniValue::VARR); std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs; uint8_t funcid; char numstr[32],funcidstr[16],origaddr[64],assetidstr[65]; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
SetCCunspents(unspentOutputs,(char *)cp->unspendableCCaddr);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
UniValue result(UniValue::VARR);
struct CCcontract_info *cpAssets, assetsC;
struct CCcontract_info *cpTokens, tokensC;
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
auto addOrders = [&](struct CCcontract_info *cp, std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it)
{
uint256 txid, hashBlock, assetid, assetid2;
int64_t price;
std::vector<uint8_t> origpubkey;
CTransaction ordertx;
uint8_t funcid, evalCode;
char numstr[32], funcidstr[16], origaddr[64], origtokenaddr[64], assetidstr[65];
txid = it->first.txhash;
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 )
{
if ( vintx.vout.size() > 0 && (funcid= DecodeAssetOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,assetid,assetid2,price,origpubkey)) != 0 )
// for logging: funcid = DecodeAssetOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey);
if (ordertx.vout.size() > 0 && (funcid = DecodeAssetTokenOpRet(ordertx.vout[ordertx.vout.size()-1].scriptPubKey, evalCode, assetid, assetid2, price, origpubkey)) != 0)
{
if ( refassetid != zero && assetid != refassetid )
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() checking ordertx.vout.size()=" << ordertx.vout.size() << " funcid=" << (char)(funcid ? funcid : ' ') << " assetid=" << assetid.GetHex() << std::endl);
if (refassetid != zero && assetid == refassetid ||
pk != CPubKey() && pk == pubkey2pk(origpubkey) && (funcid == 'S' || funcid == 's'))
{
//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");
continue;
}
if ( vintx.vout[it->first.index].nValue == 0 )
continue;
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)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));
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' )
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() it->first.index=" << it->first.index << " ordertx.vout[it->first.index].nValue=" << ordertx.vout[it->first.index].nValue << std::endl);
if (ordertx.vout[it->first.index].nValue == 0) {
LOGSTREAM("ccassets", CCLOG_DEBUG2, stream << "addOrders() order with value=0 skipped" << std::endl);
return;
}
UniValue item(UniValue::VOBJ);
funcidstr[0] = funcid;
funcidstr[1] = 0;
item.push_back(Pair("funcid", funcidstr));
item.push_back(Pair("txid", 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,"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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputsTokens, unspentOutputsDualEvalTokens, unspentOutputsCoins;
char assetsUnspendableAddr[64];
GetCCaddress(cpAssets, assetsUnspendableAddr, GetUnspendable(cpAssets, NULL));
SetCCunspents(unspentOutputsCoins, assetsUnspendableAddr,true);
char assetsTokensUnspendableAddr[64];
std::vector<uint8_t> 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);
// tokenbids:
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itCoins = unspentOutputsCoins.begin();
itCoins != unspentOutputsCoins.end();
itCoins++)
addOrders(cpAssets, itCoins);
// tokenasks:
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itTokens = unspentOutputsTokens.begin();
itTokens != unspentOutputsTokens.end();
itTokens++)
addOrders(cpAssets, itTokens);
if (additionalEvalCode != 0) { //this would be mytokenorders
char assetsDualEvalTokensUnspendableAddr[64];
// try also dual eval tokenasks (and we do not need bids):
cpAssets->additionalTokensEvalcode2 = additionalEvalCode;
GetTokensCCaddress(cpAssets, assetsDualEvalTokensUnspendableAddr, GetUnspendable(cpAssets, NULL));
SetCCunspents(unspentOutputsDualEvalTokens, assetsDualEvalTokensUnspendableAddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator itDualEvalTokens = unspentOutputsDualEvalTokens.begin();
itDualEvalTokens != unspentOutputsDualEvalTokens.end();
itDualEvalTokens++)
addOrders(cpAssets, itDualEvalTokens);
}
return(result);
}
std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description)
// not used (use TokenCreate instead)
/* std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::string description)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp,C;
@@ -213,9 +182,10 @@ std::string CreateAsset(int64_t txfee,int64_t assetsupply,std::string name,std::
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetCreateOpRet('c',Mypubkey(),name,description)));
}
return("");
}
std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total)
} */
// not used (use TokenTransfer instead)
/* std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_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;
@@ -230,11 +200,11 @@ std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> des
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
{
/*n = outputs.size();
if ( n == amounts.size() )
{
for (i=0; i<n; i++)
total += amounts[i];*/
//n = outputs.size();
//if ( n == amounts.size() )
//{
// for (i=0; i<n; i++)
// total += amounts[i];
mask = ~((1LL << mtx.vin.size()) - 1);
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,total,60)) > 0 )
{
@@ -254,9 +224,10 @@ std::string AssetTransfer(int64_t txfee,uint256 assetid,std::vector<uint8_t> des
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
}
return("");
}
} */
std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode)
// deprecated
/* std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> destpubkey,int64_t total,int32_t evalcode)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; int64_t CCchange=0,inputs=0; struct CCcontract_info *cp,C;
@@ -281,76 +252,128 @@ std::string AssetConvert(int64_t txfee,uint256 assetid,std::vector<uint8_t> dest
} else fprintf(stderr,"not enough CC asset inputs for %.8f\n",(double)total/COIN);
}
return("");
}
} */
std::string CreateBuyOffer(int64_t txfee,int64_t bidamount,uint256 assetid,int64_t pricetotal)
// rpc tokenbid implementation, locks 'bidamount' coins for the 'pricetotal' of tokens
std::string CreateBuyOffer(int64_t txfee, int64_t bidamount, uint256 assetid, int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; struct CCcontract_info *cp,C; uint256 hashBlock; CTransaction vintx; std::vector<uint8_t> origpubkey; std::string name,description;
if ( bidamount < 0 || pricetotal < 0 )
CPubKey mypk;
struct CCcontract_info *cpAssets, C;
uint256 hashBlock;
CTransaction vintx;
std::vector<uint8_t> origpubkey;
std::string name,description;
int64_t inputs;
std::cerr << "CreateBuyOffer() bidamount=" << bidamount << " numtokens(pricetotal)=" << pricetotal << std::endl;
if (bidamount < 0 || pricetotal < 0)
{
fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n",(long long)bidamount,(long long)pricetotal);
fprintf(stderr,"negative bidamount %lld, pricetotal %lld\n", (long long)bidamount, (long long)pricetotal);
return("");
}
if ( GetTransaction(assetid,vintx,hashBlock,false) == 0 )
if (GetTransaction(assetid, vintx, hashBlock, false) == 0)
{
fprintf(stderr,"cant find assetid\n");
return("");
}
if ( vintx.vout.size() > 0 && DecodeAssetCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,origpubkey,name,description) == 0 )
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey, origpubkey, name, description) == 0)
{
fprintf(stderr,"assetid isnt assetcreation txid\n");
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpAssets = CCinit(&C,EVAL_ASSETS); // NOTE: assets here!
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,bidamount+txfee,64) > 0 )
if ((inputs = AddNormalinputs(mtx, mypk, bidamount+(2*txfee), 64)) > 0)
{
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,bidamount,GetUnspendable(cp,0)));
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('b',assetid,zeroid,pricetotal,Mypubkey())));
std::cerr << "CreateBuyOffer() inputs=" << inputs << std::endl;
if (inputs < bidamount+txfee) {
std::cerr << "CreateBuyOffer(): insufficient coins to make buy offer" << std::endl;
CCerror = strprintf("insufficient coins to make buy offer");
return ("");
}
CPubKey unspendableAssetsPubkey = GetUnspendable(cpAssets, 0);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount, unspendableAssetsPubkey));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, mypk));
std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts
return FinalizeCCTx(0, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys, // TODO: actually this tx is not 'tokens', maybe it is better not to have token opret here but only asset opret.
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('b', zeroid, pricetotal, Mypubkey())))); // But still such token opret should not make problems because no token eval in these vouts
}
CCerror = strprintf("no coins found to make buy offer");
return("");
}
// rpc tokenask implementation, locks 'askamount' tokens for the 'pricetotal'
std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C;
CPubKey mypk;
uint64_t mask;
int64_t inputs, CCchange;
struct CCcontract_info *cpAssets, assetsC;
struct CCcontract_info *cpTokens, tokensC;
//std::cerr << "CreateSell() askamount=" << askamount << " pricetotal=" << pricetotal << std::endl;
if ( askamount < 0 || pricetotal < 0 )
{
if (askamount < 0 || pricetotal < 0) {
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpAssets = CCinit(&assetsC, EVAL_ASSETS); // NOTE: for signing
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0)
{
std::vector<uint8_t> vopretNonfungible;
mask = ~((1LL << mtx.vin.size()) - 1);
if ( (inputs= AddAssetInputs(cp,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) {
//askamount = inputs;
//was: askamount = inputs;
std::cerr << "CreateSell(): insufficient tokens for ask" << std::endl;
CCerror = strprintf("insufficient tokens for ask");
return ("");
}
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,GetUnspendable(cp,0)));
if ( inputs > askamount )
// 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_ASSETS,CCchange,mypk));
opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey());
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
if (CCchange != 0)
// change to single-eval or non-fungible token vout (although for non-fungible token change currently is not possible)
mtx.vout.push_back(MakeTokensCC1vout((cpAssets->additionalTokensEvalcode2) ? cpAssets->additionalTokensEvalcode2 : EVAL_TOKENS, CCchange, mypk));
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(unspendableAssetsPubkey);
return FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()))));
}
else {
fprintf(stderr, "need some assets to place ask\n");
fprintf(stderr, "need some tokens to place ask\n");
}
}
else { // dimxy added 'else', because it was misleading message before
@@ -359,197 +382,401 @@ std::string CreateSell(int64_t txfee,int64_t askamount,uint256 assetid,int64_t p
return("");
}
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
std::string CreateSwap(int64_t txfee,int64_t askamount,uint256 assetid,uint256 assetid2,int64_t pricetotal)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t inputs,CCchange; CScript opret; struct CCcontract_info *cp,C;
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
fprintf(stderr,"asset swaps disabled\n");
return("");
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
if ( askamount < 0 || pricetotal < 0 )
{
fprintf(stderr,"negative askamount %lld, askamount %lld\n",(long long)pricetotal,(long long)askamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
cp = CCinit(&C, EVAL_ASSETS);
if ( txfee == 0 )
txfee = 10000;
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
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)
{
if ( inputs < askamount )
askamount = inputs;
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,GetUnspendable(cp,0)));
if ( inputs > askamount )
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
if (inputs < askamount) {
//was: askamount = inputs;
std::cerr << "CreateSwap(): insufficient tokens for ask" << std::endl;
CCerror = strprintf("insufficient tokens for ask");
return ("");
}
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
CPubKey unspendablePubkey = GetUnspendable(cp, 0);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, askamount, unspendablePubkey));
if (inputs > askamount)
CCchange = (inputs - askamount);
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
if ( assetid2 == zeroid )
opret = EncodeAssetOpRet('s',assetid,zeroid,pricetotal,Mypubkey());
else
{
opret = EncodeAssetOpRet('e',assetid,assetid2,pricetotal,Mypubkey());
}
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk));
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
std::vector<CPubKey> voutTokenPubkeys; // should be empty - no token vouts
if (assetid2 == zeroid) {
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('s', zeroid, pricetotal, Mypubkey()));
}
else {
opret = EncodeTokenOpRet(assetid, voutTokenPubkeys,
EncodeAssetOpRet('e', assetid2, pricetotal, Mypubkey()));
}
////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,opret));
}
else {
fprintf(stderr, "need some assets to place ask\n");
}
} */
}
else { // dimxy added 'else', because it was misleading message before
fprintf(stderr,"need some native coins to place ask\n");
}
return("");
}
} ////////////////////////// NOT IMPLEMENTED YET/////////////////////////////////
// unlocks coins
std::string CancelBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t bidamount; CPubKey mypk; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
CTransaction vintx; uint64_t mask;
uint256 hashBlock; int64_t bidamount;
CPubKey mypk; struct CCcontract_info *cpAssets, C;
uint8_t funcid,dummyEvalCode; uint256 dummyAssetid, dummyAssetid2; int64_t dummyPrice; std::vector<uint8_t> dummyOrigpubkey;
cpAssets = CCinit(&C, EVAL_ASSETS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(bidtxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{
std::vector<uint8_t> vopretNonfungible;
GetNonfungibleData(assetid, vopretNonfungible);
bidamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(bidtxid,0,CScript()));
mtx.vin.push_back(CTxIn(bidtxid, 0, CScript())); // coins in Assets
if((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0)
{
if (funcid == 's') mtx.vin.push_back(CTxIn(bidtxid, 1, CScript())); // spend marker if funcid='b'
else if (funcid=='S') mtx.vin.push_back(CTxIn(bidtxid, 3, CScript())); // spend marker if funcid='B'
}
mtx.vout.push_back(CTxOut(bidamount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('o',assetid,zeroid,0,Mypubkey())));
mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
std::vector<CPubKey> voutTokenPubkeys; // should be empty, no token vouts
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('o', zeroid, 0, Mypubkey())))));
}
}
return("");
}
//unlocks tokens
std::string CancelSell(int64_t txfee,uint256 assetid,uint256 asktxid)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint64_t mask; uint256 hashBlock; int64_t askamount; CPubKey mypk; struct CCcontract_info *cp,C;
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
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<uint8_t> dummyOrigpubkey;
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(asktxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(asktxid, vintx, hashBlock, false) != 0)
{
std::vector<uint8_t> vopretNonfungible;
GetNonfungibleData(assetid, vopretNonfungible);
askamount = vintx.vout[0].nValue;
mtx.vin.push_back(CTxIn(asktxid,0,CScript()));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,askamount,mypk));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('x',assetid,zeroid,0,Mypubkey())));
mtx.vin.push_back(CTxIn(asktxid, 0, CScript()));
if ((funcid=DecodeAssetTokenOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, dummyEvalCode, dummyAssetid, dummyAssetid2, dummyPrice, dummyOrigpubkey))!=0)
{
if (funcid == 's')
mtx.vin.push_back(CTxIn(asktxid, 1, CScript())); // marker if funcid='s'
else if (funcid=='S')
mtx.vin.push_back(CTxIn(asktxid, 3, CScript())); // marker if funcid='S'
}
if (vopretNonfungible.size() > 0)
cpAssets->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<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(mypk);
// this is only for unspendable addresses:
//CCaddr2set(cpTokens, EVAL_ASSETS, mypk, myPrivkey, myCCaddr); //do we need this? Seems FinalizeCCTx can attach to any evalcode cc addr by calling Getscriptaddress
uint8_t unspendableAssetsPrivkey[32];
char unspendableAssetsAddr[64];
// init assets 'unspendable' privkey and pubkey
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
// add additional eval-tokens unspendable assets privkey:
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('x', zeroid, 0, Mypubkey())))));
}
}
return("");
}
//send tokens, receive coins:
std::string FillBuyOffer(int64_t txfee,uint256 assetid,uint256 bidtxid,int64_t fillamount)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx; uint256 hashBlock; CPubKey mypk; std::vector<uint8_t> origpubkey; int32_t bidvout=0; uint64_t mask; int64_t origprice,bidamount,paid_amount,remaining_required,inputs,CCchange=0; struct CCcontract_info *cp,C;
if ( fillamount < 0 )
CTransaction vintx;
uint256 hashBlock;
CPubKey mypk;
std::vector<uint8_t> origpubkey;
int32_t bidvout=0;
uint64_t mask;
int64_t origprice, bidamount, paid_amount, remaining_required, inputs, CCchange=0;
struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
if (fillamount < 0)
{
fprintf(stderr,"negative fillamount %lld\n",(long long)fillamount);
fprintf(stderr,"negative fillamount %lld\n", (long long)fillamount);
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 )
if (AddNormalinputs(mtx, mypk, 2*txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(bidtxid,vintx,hashBlock,false) != 0 )
if (GetTransaction(bidtxid, vintx, hashBlock, false) != 0)
{
bidamount = vintx.vout[bidvout].nValue;
SetAssetOrigpubkey(origpubkey,origprice,vintx);
mtx.vin.push_back(CTxIn(bidtxid,bidvout,CScript()));
if ( (inputs= AddAssetInputs(cp,mtx,mypk,assetid,fillamount,60)) > 0 )
SetAssetOrigpubkey(origpubkey, origprice, vintx);
mtx.vin.push_back(CTxIn(bidtxid, bidvout, CScript())); // Coins on Assets unspendable
std::vector<uint8_t> vopretNonfungible;
if ((inputs = AddTokenCCInputs(cpTokens, mtx, mypk, assetid, fillamount, 60, vopretNonfungible)) > 0)
{
if ( inputs < fillamount )
fillamount = inputs;
SetBidFillamounts(paid_amount,remaining_required,bidamount,fillamount,origprice);
if ( inputs > fillamount )
if (inputs < fillamount) {
std::cerr << "FillBuyOffer(): insufficient tokens to fill buy offer" << std::endl;
CCerror = strprintf("insufficient tokens to fill buy offer");
return ("");
}
SetBidFillamounts(paid_amount, remaining_required, bidamount, fillamount, origprice);
uint8_t additionalTokensEvalcode2 = 0;
if (vopretNonfungible.size() > 0)
additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
if (inputs > fillamount)
CCchange = (inputs - fillamount);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,bidamount - paid_amount,GetUnspendable(cp,0)));
mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,fillamount,pubkey2pk(origpubkey)));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
fprintf(stderr,"remaining %llu -> origpubkey\n",(long long)remaining_required);
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet('B',assetid,zeroid,remaining_required,origpubkey)));
} else return("dont have any assets to fill bid\n");
uint8_t unspendableAssetsPrivkey[32];
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, bidamount - paid_amount, unspendableAssetsPk)); // vout0 coins remainder
mtx.vout.push_back(CTxOut(paid_amount,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); // vout1 coins to normal
mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, fillamount, pubkey2pk(origpubkey))); // vout2 single-eval tokens sent to the originator
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, txfee, origpubkey)); // vout3 marker to origpubkey
if (CCchange != 0)
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, CCchange, mypk)); // vout4 change in single-eval tokens
fprintf(stderr,"FillBuyOffer() remaining %llu -> origpubkey\n", (long long)remaining_required);
char unspendableAssetsAddr[64];
cpAssets = CCinit(&assetsC, EVAL_ASSETS);
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
// add additional unspendable addr from Assets:
CCaddr2set(cpTokens, EVAL_ASSETS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
// token vout verification pubkeys:
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(origpubkey));
return(FinalizeCCTx(mask, cpTokens, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('B', zeroid, remaining_required, origpubkey)))));
} else return("dont have any assets to fill bid");
}
}
return("no normal coins left");
}
std::string FillSell(int64_t txfee,uint256 assetid,uint256 assetid2,uint256 asktxid,int64_t fillunits)
// send coins, receive tokens
std::string FillSell(int64_t txfee, uint256 assetid, uint256 assetid2, uint256 asktxid, int64_t fillunits)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction vintx,filltx; uint256 hashBlock; CPubKey mypk; std::vector<uint8_t> origpubkey; double dprice; uint64_t mask; int32_t askvout=0; int64_t received_assetoshis,total_nValue,orig_assetoshis,paid_nValue,remaining_nValue,inputs,CCchange=0; struct CCcontract_info *cp,C;
if ( fillunits < 0 )
CTransaction vintx,filltx;
uint256 hashBlock;
CPubKey mypk;
std::vector<uint8_t> origpubkey;
double dprice;
uint64_t mask = 0;
int32_t askvout = 0;
int64_t received_assetoshis, total_nValue, orig_assetoshis, paid_nValue, remaining_nValue, inputs, CCchange=0;
//struct CCcontract_info *cpTokens, tokensC;
struct CCcontract_info *cpAssets, assetsC;
if (fillunits < 0)
{
CCerror = strprintf("negative fillunits %lld\n",(long long)fillunits);
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
if ( assetid2 != zeroid )
if (assetid2 != zeroid)
{
CCerror = "asset swaps disabled";
fprintf(stderr,"%s\n",CCerror.c_str());
return("");
}
cp = CCinit(&C,EVAL_ASSETS);
if ( txfee == 0 )
std::vector<uint8_t> 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,txfee,3) > 0 )
{
mask = ~((1LL << mtx.vin.size()) - 1);
if ( GetTransaction(asktxid,vintx,hashBlock,false) != 0 )
//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;
SetAssetOrigpubkey(origpubkey,total_nValue,vintx);
SetAssetOrigpubkey(origpubkey, total_nValue, vintx);
dprice = (double)total_nValue / orig_assetoshis;
paid_nValue = dprice * fillunits;
mtx.vin.push_back(CTxIn(asktxid,askvout,CScript()));
if ( assetid2 != zeroid )
inputs = AddAssetInputs(cp,mtx,mypk,assetid2,paid_nValue,60);
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 )
if (inputs > 0)
{
if ( inputs < paid_nValue )
paid_nValue = inputs;
if ( assetid2 != zeroid )
SetSwapFillamounts(received_assetoshis,remaining_nValue,orig_assetoshis,paid_nValue,total_nValue);
else SetAskFillamounts(received_assetoshis,remaining_nValue,orig_assetoshis,paid_nValue,total_nValue);
if ( assetid2 != zeroid && inputs > paid_nValue )
if (inputs < paid_nValue) {
std::cerr << "FillSell(): insufficient coins to fill sell" << std::endl;
CCerror = strprintf("insufficient coins to fill sell");
return ("");
}
// cc vin should be after normal vin
mtx.vin.push_back(CTxIn(asktxid, askvout, CScript()));
if (assetid2 != zeroid)
SetSwapFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue); //not implemented correctly yet
else
SetAskFillamounts(received_assetoshis, remaining_nValue, orig_assetoshis, paid_nValue, total_nValue);
if (assetid2 != zeroid && inputs > paid_nValue)
CCchange = (inputs - paid_nValue);
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,orig_assetoshis - received_assetoshis,GetUnspendable(cp,0)));
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,received_assetoshis,mypk));
if ( assetid2 != zeroid )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,paid_nValue,origpubkey));
else mtx.vout.push_back(CTxOut(paid_nValue,CScript() << origpubkey << OP_CHECKSIG));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
return(FinalizeCCTx(mask,cp,mtx,mypk,txfee,EncodeAssetOpRet(assetid2!=zeroid?'E':'S',assetid,assetid2,remaining_nValue,origpubkey)));
// vout.0 tokens remainder to unspendable cc addr:
mtx.vout.push_back(MakeTokensCC1vout(EVAL_ASSETS, additionalTokensEvalcode2, orig_assetoshis - received_assetoshis, GetUnspendable(cpAssets, NULL)));
//vout.1 purchased tokens to self token single-eval or dual-eval token+nonfungible cc addr:
mtx.vout.push_back(MakeTokensCC1vout(additionalTokensEvalcode2 == 0 ? EVAL_TOKENS : additionalTokensEvalcode2, received_assetoshis, mypk));
if (assetid2 != zeroid) {
std::cerr << "FillSell() WARNING: asset swap not implemented yet! (paid_nValue)" << std::endl;
// TODO: change MakeCC1vout appropriately when implementing:
//mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, paid_nValue, origpubkey)); //vout.2 tokens... (swap is not implemented yet)
}
else {
//std::cerr << "FillSell() paid_value=" << paid_nValue << " origpubkey=" << HexStr(pubkey2pk(origpubkey)) << std::endl;
mtx.vout.push_back(CTxOut(paid_nValue, CScript() << origpubkey << OP_CHECKSIG)); //vout.2 coins to tokens seller's normal addr
}
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,txfee,origpubkey)); //vout.3 marker to origpubkey
// not implemented
if (CCchange != 0) {
std::cerr << "FillSell() WARNING: asset swap not implemented yet! (CCchange)" << std::endl;
// TODO: change MakeCC1vout appropriately when implementing:
//mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS, CCchange, mypk)); //vout.3 coins in Assets cc addr (swap not implemented)
}
uint8_t unspendableAssetsPrivkey[32];
char unspendableAssetsAddr[64];
// init assets 'unspendable' privkey and pubkey
CPubKey unspendableAssetsPk = GetUnspendable(cpAssets, unspendableAssetsPrivkey);
GetCCaddress(cpAssets, unspendableAssetsAddr, unspendableAssetsPk);
// add additional eval-tokens unspendable assets privkey:
CCaddr2set(cpAssets, EVAL_TOKENS, unspendableAssetsPk, unspendableAssetsPrivkey, unspendableAssetsAddr);
// vout verification pubkeys:
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(mypk);
cpAssets->additionalTokensEvalcode2 = additionalTokensEvalcode2;
return(FinalizeCCTx(mask, cpAssets, mtx, mypk, txfee,
EncodeTokenOpRet(assetid, voutTokenPubkeys,
std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet(assetid2 != zeroid ? 'E' : 'S', assetid2, remaining_nValue, origpubkey)))));
} else {
CCerror = strprintf("filltx not enough utxos");
fprintf(stderr,"%s\n", CCerror.c_str());
}
}
}
//}
return("");
}

View File

@@ -21,7 +21,7 @@
#define CHANNELS_MAXPAYMENTS 1000
bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment);
std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 tokenid);
std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret);
std::string ChannelClose(uint64_t txfee,uint256 opentxid);
std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid);

View File

@@ -30,6 +30,8 @@
#include "CCMarmara.h"
#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.
@@ -62,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
@@ -74,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
@@ -222,8 +222,87 @@ uint8_t GatewaysCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0
#undef FUNCNAME
#undef EVALCODE
// Tokens
#define FUNCNAME IsTokensInput
#define EVALCODE EVAL_TOKENS
const char *TokensCCaddr = "RAMvUfoyURBRxAdVeTMHxn3giJZCFWeha2";
const char *TokensNormaladdr = "RCNgAngYAdrfzujYyPgfbjCGNVQZzCgTad";
char TokensCChexstr[67] = { "03e6191c70c9c9a28f9fd87089b9488d0e6c02fb629df64979c9cdb6b2b4a68d95" };
uint8_t TokensCCpriv[32] = { 0x1d, 0x0d, 0x0d, 0xce, 0x2d, 0xd2, 0xe1, 0x9d, 0xf5, 0xb6, 0x26, 0xd5, 0xad, 0xa0, 0xf0, 0x0a, 0xdd, 0x7a, 0x72, 0x7d, 0x17, 0x35, 0xb5, 0xe3, 0x2c, 0x6c, 0xa9, 0xa2, 0x03, 0x16, 0x4b, 0xcf };
#include "CCcustom.inc"
#undef FUNCNAME
#undef EVALCODE
#define FUNCNAME IsCClibInput
#define EVALCODE EVAL_FIRSTUSER
const char *CClibNormaladdr = "RVVeUg43rNcq3mZFnvZ8yqagyzqFgUnq4u";
char CClibCChexstr[67] = { "032447d97655da079729dc024c61088ea415b22f4c15d4810ddaf2069ac6468d2f" };
uint8_t CClibCCpriv[32] = { 0x57, 0xcf, 0x49, 0x71, 0x7d, 0xb4, 0x15, 0x1b, 0x4f, 0x98, 0xc5, 0x45, 0x8d, 0x26, 0x52, 0x4b, 0x7b, 0xe9, 0xbd, 0x55, 0xd8, 0x20, 0xd6, 0xc4, 0x82, 0x0f, 0xf5, 0xec, 0x6c, 0x1c, 0xa0, 0xc0 };
#include "CCcustom.inc"
#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; 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
{
strcpy(cp->CChexstr,CClibCChexstr);
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);
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; i<evalcode; i++)
{
vcalc_sha256(0,hash,cp->CCpriv,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 )
{
@@ -347,6 +426,27 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode)
cp->validate = GatewaysValidate;
cp->ismyvin = IsGatewaysInput;
break;
case EVAL_TOKENS:
strcpy(cp->unspendableCCaddr, TokensCCaddr);
strcpy(cp->normaladdr, TokensNormaladdr);
strcpy(cp->CChexstr, TokensCChexstr);
memcpy(cp->CCpriv, TokensCCpriv, 32);
cp->validate = TokensValidate;
cp->ismyvin = IsTokensInput;
break;
case EVAL_IMPORTGATEWAY:
strcpy(cp->unspendableCCaddr, ImportGatewayCCaddr);
strcpy(cp->normaladdr, ImportGatewayNormaladdr);
strcpy(cp->CChexstr, ImportGatewayCChexstr);
memcpy(cp->CCpriv, ImportGatewayCCpriv, 32);
cp->validate = ImportGatewayValidate;
cp->ismyvin = IsImportGatewayInput;
break;
default:
if ( CClib_initcp(cp,evalcode) < 0 )
return(0);
break;
}
return(cp);
}

View File

@@ -42,6 +42,7 @@ one other technical note is that komodod has the insight-explorer extensions bui
#include <cryptoconditions.h>
#include "../script/standard.h"
#include "../base58.h"
#include "../key.h"
#include "../core_io.h"
#include "../script/sign.h"
#include "../wallet/wallet.h"
@@ -50,19 +51,45 @@ 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"
extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE;
extern uint32_t ASSETCHAINS_CC;
extern char ASSETCHAINS_SYMBOL[];
extern std::string CCerror;
#define CC_BURNPUBKEY "02deaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddeaddead"
#define CC_MAXVINS 1024
#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 "../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<std::pair<uint8_t, std::vector<uint8_t>>> &oprets, uint8_t id, std::vector<uint8_t> &vopret) {
vopret.clear();
for(auto p : oprets) if (p.first == id) { vopret = p.second; return true; }
return false;
}
struct CC_utxo
{
@@ -84,12 +111,32 @@ struct CC_meta
struct CCcontract_info
{
char unspendableCCaddr[64],CChexstr[72],normaladdr[64],unspendableaddr2[64],unspendableaddr3[64];
uint8_t CCpriv[32],unspendablepriv2[32],unspendablepriv3[32];
CPubKey unspendablepk2,unspendablepk3;
bool (*validate)(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn);
bool (*ismyvin)(CScript const& scriptSig);
uint8_t evalcode,evalcode2,evalcode3,didinit;
// 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]; uint8_t coins1of2priv[32];
// the same for tokens 1of2 keys cc
char tokens1of2addr[64];
CPubKey tokens1of2pk[2];
// 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 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;
bool (*validate)(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); // cc contract tx validation callback
bool (*ismyvin)(CScript const& scriptSig); // checks if evalcode is present in the scriptSig param
uint8_t didinit;
};
struct CCcontract_info *CCinit(struct CCcontract_info *cp,uint8_t evalcode);
@@ -100,64 +147,115 @@ struct oracleprice_info
int32_t height;
};
typedef std::vector<uint8_t> vscript_t;
#ifdef ENABLE_WALLET
extern CWallet* pwalletMain;
#endif
//extern CCoinsViewCache *pcoinsTip;
bool GetAddressUnspent(uint160 addressHash, int type,std::vector<std::pair<CAddressUnspentKey,CAddressUnspentValue> > &unspentOutputs);
CBlockIndex *komodo_getblockindex(uint256 hash);
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,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);
//uint64_t myGettxout(uint256 hash,int32_t n);
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<CPubKey>& 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<std::vector<unsigned char>> &preConditions, std::vector<std::vector<unsigned char>> &params);
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::vector<struct oracle_merklepair>publishers);
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);
bool DecodeHexTx(CTransaction& tx, const std::string& strHexTx);
bool DecodeAssetCreateOpRet(const CScript &scriptPubKey,std::vector<uint8_t> &origpubkey,std::string &name,std::string &description);
uint8_t DecodeAssetOpRet(const CScript &scriptPubKey,uint256 &assetid,uint256 &assetid2,int64_t &price,std::vector<uint8_t> &origpubkey);
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 <uint8_t>&data);
int32_t oracle_format(uint256 *hashp,int64_t *valp,char *str,uint8_t fmt,uint8_t *data,int32_t offset,int32_t datalen);
CScript EncodeAssetOpRet(uint8_t funcid,uint256 assetid,uint256 assetid2,int64_t price,std::vector<uint8_t> origpubkey);
//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<uint8_t> payload);
int32_t payments_parsehexdata(std::vector<uint8_t> &hexdata,cJSON *item,int32_t len);
int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex);
CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, vscript_t vopretNonfungible);
CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, std::vector<std::pair<uint8_t, vscript_t>> oprets);
CScript EncodeTokenImportOpRet(std::vector<uint8_t> origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector<std::pair<uint8_t, vscript_t>> oprets);
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::pair<uint8_t, vscript_t> opretWithId);
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> oprets);
int64_t AddCClibtxfee(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk);
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
uint8_t DecodeTokenImportOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible);
bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector<CPubKey> &vinPubkeys);
// CCcustom
CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv);
//uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<uint8_t> &vopret1, std::vector<uint8_t> &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);
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk);
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2);
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk, const std::vector<std::vector<unsigned char>>* vData = NULL);
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2, const std::vector<std::vector<unsigned char>>* vData = NULL);
CC *MakeCCcond1(uint8_t evalcode,CPubKey pk);
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,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<uint8_t> pubkey);
int64_t CCfullsupply(uint256 tokenid);
int64_t CCtoken_balance(char *destaddr,uint256 tokenid);
@@ -168,18 +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<uint8_t> 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<uint8_t> &proofData, std::vector<uint256> &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);
std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret);
void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr);
void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,char *coinaddr);
extern std::vector<CPubKey> NULL_pubkeys;
std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector<CPubKey> pubkeys = NULL_pubkeys);
void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr,bool CCflag = true);
void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &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);
@@ -187,5 +291,33 @@ bits256 curve25519_basepoint9();
bits256 curve25519(bits256 mysecret,bits256 basepoint);
void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uint8_t *src,int32_t len);
bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen);
UniValue ValueFromAmount(const CAmount& amount);
// 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 <class T>
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

956
src/cc/CCtokens.cpp Normal file
View File

@@ -0,0 +1,956 @@
/******************************************************************************
* Copyright © 2014-2018 The SuperNET Developers. *
* *
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
* the top-level directory of this distribution for the individual copyright *
* holder information and the developer policies on copyright and licensing. *
* *
* Unless otherwise agreed in a custom licensing agreement, no part of the *
* SuperNET software, including this file may be copied, modified, propagated *
* or distributed except according to the terms contained in the LICENSE file *
* *
* Removal or modification of this copyright notice is prohibited. *
* *
******************************************************************************/
#include "CCtokens.h"
/* TODO: correct this:
-----------------------------
The SetTokenFillamounts() and ValidateTokenRemainder() work in tandem to calculate the vouts for a fill and to validate the vouts, respectively.
This pair of functions are critical to make sure the trading is correct and is the trickiest part of the tokens contract.
//vin.0: normal input
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
//vin.2+: valid CC output satisfies buyoffer (*tx.vin[2])->nValue
//vout.0: remaining amount of bid to unspendable
//vout.1: vin.1 value to signer of vin.2
//vout.2: vin.2 tokenoshis to original pubkey
//vout.3: CC output for tokenoshis change (if any)
//vout.4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [tokenid] [remaining token required] [origpubkey]
ValidateTokenRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits);
Yes, this is quite confusing...
In ValidateTokenRemainder the naming convention is nValue is the coin/token with the offer on the books and "units" is what it is being paid in. The high level check is to make sure we didnt lose any coins or tokens, the harder to validate is the actual price paid as the "orderbook" is in terms of the combined nValue for the combined totalunits.
We assume that the effective unit cost in the orderbook is valid and that that amount was paid and also that any remainder will be close enough in effective unit cost to not matter. At the edge cases, this will probably be not true and maybe some orders wont be practically fillable when reduced to fractional state. However, the original pubkey that created the offer can always reclaim it.
------------------------------
*/
// tx validation
bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
{
static uint256 zero;
CTxDestination address; CTransaction vinTx, createTx; uint256 hashBlock, tokenid, tokenid2;
int32_t i, starti, numvins, numvouts, preventCCvins, preventCCvouts;
int64_t remaining_price, nValue, tokenoshis, outputs, inputs, tmpprice, totalunits, ignore;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
vscript_t /*vopretExtra,*/ tmporigpubkey, ignorepubkey;
uint8_t funcid, evalCodeInOpret;
char destaddr[64], origaddr[64], CCaddr[64];
std::vector<CPubKey> voutTokenPubkeys, vinTokenPubkeys;
if (strcmp(ASSETCHAINS_SYMBOL, "ROGUE") == 0 && chainActive.Height() <= 12500)
return true;
numvins = tx.vin.size();
numvouts = tx.vout.size();
outputs = inputs = 0;
preventCCvins = preventCCvouts = -1;
// check boundaries:
if (numvouts < 1)
return eval->Invalid("no vouts");
if ((funcid = DecodeTokenOpRet(tx.vout[numvouts - 1].scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets)) == 0)
return eval->Invalid("TokenValidate: invalid opreturn payload");
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokensValidate funcId=" << (char)(funcid?funcid:' ') << " evalcode=" << std::hex << (int)cp->evalcode << std::endl);
if (eval->GetTxUnconfirmed(tokenid, createTx, hashBlock) == 0)
return eval->Invalid("cant find token create txid");
//else if (IsCCInput(tx.vin[0].scriptSig) != 0)
// return eval->Invalid("illegal token vin0"); // <-- this validation was removed because some token tx might not have normal vins
else if (funcid != 'c')
{
if (tokenid == zeroid)
return eval->Invalid("illegal tokenid");
else if (!TokensExactAmounts(true, cp, inputs, outputs, eval, tx, tokenid)) {
if (!eval->Valid())
return false; //TokenExactAmounts must call eval->Invalid()!
else
return eval->Invalid("tokens cc inputs != cc outputs");
}
}
// validate spending from token cc addr: allowed only for burned non-fungible tokens:
if (ExtractTokensCCVinPubkeys(tx, vinTokenPubkeys) && std::find(vinTokenPubkeys.begin(), vinTokenPubkeys.end(), GetUnspendable(cp, NULL)) != vinTokenPubkeys.end()) {
// validate spending from token unspendable cc addr:
int64_t burnedAmount = HasBurnedTokensvouts(cp, eval, tx, tokenid);
if (burnedAmount > 0) {
vscript_t vopretNonfungible;
GetNonfungibleData(tokenid, vopretNonfungible);
if( vopretNonfungible.empty() )
return eval->Invalid("spending cc marker not supported for fungible tokens");
}
}
switch (funcid)
{
case 'c': // create wont be called to be verified as it has no CC inputs
//vin.0: normal input
//vout.0: issuance tokenoshis to CC
//vout.1: normal output for change (if any)
//vout.n-1: opreturn EVAL_TOKENS 'c' <tokenname> <description>
//if (evalCodeInOpret != EVAL_TOKENS)
// return eval->Invalid("unexpected TokenValidate for createtoken");
//else
return true;
case 't': // transfer
//vin.0: normal input
//vin.1 .. vin.n-1: valid CC outputs
//vout.0 to n-2: tokenoshis output to CC
//vout.n-2: normal output for change (if any)
//vout.n-1: opreturn <other evalcode> 't' tokenid <other contract payload>
if (inputs == 0)
return eval->Invalid("no token inputs for transfer");
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "token transfer preliminarily validated inputs=" << inputs << "->outputs=" << outputs << " preventCCvins=" << preventCCvins<< " preventCCvouts=" << preventCCvouts << std::endl);
break; // breaking to other contract validation...
default:
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "illegal tokens funcid=" << (char)(funcid?funcid:' ') << std::endl);
return eval->Invalid("unexpected token funcid");
}
// forward validation if evalcode in opret is not EVAL_TOKENS
// init for forwarding validation call
//if (evalCodeInOpret != EVAL_TOKENS) { // TODO: should we check also only allowed for tokens evalcodes, like EVAL_ASSETS, EVAL_GATEWAYS?
// struct CCcontract_info *cpOther = NULL, C;
// cpOther = CCinit(&C, evalCodeInOpret);
// if (cpOther)
// return cpOther->validate(cpOther, eval, tx, nIn);
// else
// return eval->Invalid("unsupported evalcode in opret");
//}
return true;
// what does this do?
// return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
}
// helper funcs:
// extract cc token vins' pubkeys:
bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector<CPubKey> &vinPubkeys) {
bool found = false;
CPubKey pubkey;
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
vinPubkeys.clear();
for (int32_t i = 0; i < tx.vin.size(); i++)
{
// check for cc token vins:
if( (*cpTokens->ismyvin)(tx.vin[i].scriptSig) )
{
auto findEval = [](CC *cond, struct CCVisitor _) {
bool r = false;
if (cc_typeId(cond) == CC_Secp256k1) {
*(CPubKey*)_.context = buf2pk(cond->publicKey);
//std::cerr << "findEval found pubkey=" << HexStr(*(CPubKey*)_.context) << std::endl;
r = true;
}
// false for a match, true for continue
return r ? 0 : 1;
};
CC *cond = GetCryptoCondition(tx.vin[i].scriptSig);
if (cond) {
CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey };
bool out = !cc_visit(cond, visitor);
cc_free(cond);
if (pubkey.IsValid()) {
vinPubkeys.push_back(pubkey);
found = true;
}
}
}
}
return found;
}
// this is just for log messages indentation fur debugging recursive calls:
thread_local uint32_t tokenValIndentSize = 0;
// validates opret for token tx:
uint8_t ValidateTokenOpret(CTransaction tx, uint256 tokenid) {
uint256 tokenidOpret = zeroid;
uint8_t funcid;
uint8_t dummyEvalCode;
std::vector<CPubKey> voutPubkeysDummy;
std::vector<std::pair<uint8_t, vscript_t>> opretsDummy;
// this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
if (tx.vout.size() == 0)
return (uint8_t)0;
if ((funcid = DecodeTokenOpRet(tx.vout.back().scriptPubKey, dummyEvalCode, tokenidOpret, voutPubkeysDummy, opretsDummy)) == 0)
{
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "ValidateTokenOpret() DecodeTokenOpret could not parse opret for txid=" << tx.GetHash().GetHex() << std::endl);
return (uint8_t)0;
}
else if (funcid == 'c')
{
if (tokenid != zeroid && tokenid == tx.GetHash()) {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is tokenbase 'c' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
return funcid;
}
else {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenbase txid=" << tx.GetHash().GetHex() << std::endl);
}
}
else if (funcid == 'i')
{
if (tokenid != zeroid && tokenid == tx.GetHash()) {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is import 'i' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
return funcid;
}
else {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my import txid=" << tx.GetHash().GetHex() << std::endl);
}
}
else if (funcid == 't')
{
//std::cerr << indentStr << "ValidateTokenOpret() tokenid=" << tokenid.GetHex() << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl;
if (tokenid != zeroid && tokenid == tokenidOpret) {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() this is a transfer 't' tx, txid=" << tx.GetHash().GetHex() << " returning true" << std::endl);
return funcid;
}
else {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not my tokenid=" << tokenidOpret.GetHex() << std::endl);
}
}
else {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "ValidateTokenOpret() not supported funcid=" << (char)funcid << " tokenIdOpret=" << tokenidOpret.GetHex() << " txid=" << tx.GetHash().GetHex() << std::endl);
}
return (uint8_t)0;
}
// remove token->unspendablePk (it is only for marker usage)
void FilterOutTokensUnspendablePk(const std::vector<CPubKey> &sourcePubkeys, std::vector<CPubKey> &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<std::pair<uint8_t, vscript_t>> &oprets, vscript_t &vopret) {
vopret.clear();
if (oprets.size() > 2)
LOGSTREAM("cctokens", CCLOG_INFO, stream << "FilterOutNonCCOprets() warning!! oprets.size > 2 currently not supported" << oprets.size() << std::endl);
for (auto o : oprets) {
if (o.first < OPRETID_FIRSTNONCCDATA) { // skip burn, import, etc opret data
vopret = o.second; // return first contract opret (more than 1 is not supported yet)
break;
}
}
}
// Checks if the vout is a really Tokens CC vout
// also checks tokenid in opret or txid if this is 'c' tx
// goDeeper is true: the func also validates amounts of the passed transaction:
// it should be either sum(cc vins) == sum(cc vouts) or the transaction is the 'tokenbase' ('c') tx
// checkPubkeys is true: validates if the vout is token vout1 or token vout1of2. Should always be true!
int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true*/, struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, int32_t v, uint256 reftokenid)
{
// this is just for log messages indentation fur debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() entered for txid=" << tx.GetHash().GetHex() << " v=" << v << " for tokenid=" << reftokenid.GetHex() << std::endl);
int32_t n = tx.vout.size();
// just check boundaries:
if (n == 0 || v < 0 || v >= n-1) {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "isTokensvout() incorrect params: (n == 0 or v < 0 or v >= n-1)" << " v=" << v << " n=" << n << " returning 0" << std::endl);
return(0);
}
if (tx.vout[v].scriptPubKey.IsPayToCryptoCondition())
{
if (goDeeper) {
//validate all tx
int64_t myCCVinsAmount = 0, myCCVoutsAmount = 0;
tokenValIndentSize++;
// false --> because we already at the 1-st level ancestor tx and do not need to dereference ancestors of next levels
bool isEqual = TokensExactAmounts(false, cp, myCCVinsAmount, myCCVoutsAmount, eval, tx, reftokenid);
tokenValIndentSize--;
if (!isEqual) {
// if ccInputs != ccOutputs and it is not the tokenbase tx
// this means it is possibly a fake tx (dimxy):
if (reftokenid != tx.GetHash()) { // checking that this is the true tokenbase tx, by verifying that funcid=c, is done further in this function (dimxy)
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() warning: for the verified tx detected a bad vintx=" << tx.GetHash().GetHex() << ": cc inputs != cc outputs and not the 'tokenbase' tx, skipping the verified tx" << std::endl);
return 0;
}
}
}
// token opret most important checks (tokenid == reftokenid, tokenid is non-zero, tx is 'tokenbase'):
const uint8_t funcId = ValidateTokenOpret(tx, reftokenid);
//std::cerr << indentStr << "IsTokensvout() ValidateTokenOpret returned=" << (char)(funcId?funcId:' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl;
if (funcId != 0) {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() ValidateTokenOpret returned not-null funcId=" << (char)(funcId ? funcId : ' ') << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
uint8_t dummyEvalCode;
uint256 tokenIdOpret;
std::vector<CPubKey> voutPubkeys, voutPubkeysInOpret;
vscript_t vopretExtra, vopretNonfungible;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
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<std::pair<CTxOut, std::string>> 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) {
// 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")) );
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 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) {
// 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/three-eval token change?
std::vector<CPubKey> vinPubkeys, vinPubkeysUnfiltered;
ExtractTokensCCVinPubkeys(tx, vinPubkeysUnfiltered);
FilterOutTokensUnspendablePk(vinPubkeysUnfiltered, vinPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there)
for(std::vector<CPubKey>::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); 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 (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 {
CPubKey origPubkey;
vscript_t vorigPubkey;
std::string dummyName, dummyDescription;
std::vector<std::pair<uint8_t, vscript_t>> 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());
}
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
return(0);
}
// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs)
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid)
{
CTransaction vinTx;
uint256 hashBlock;
int64_t tokenoshis;
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
int32_t numvins = tx.vin.size();
int32_t numvouts = tx.vout.size();
inputs = outputs = 0;
// this is just for log messages indentation for debugging recursive calls:
std::string indentStr = std::string().append(tokenValIndentSize, '.');
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokensExactAmounts() entered for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
for (int32_t i = 0; i<numvins; i++)
{ // check for additional contracts which may send tokens to the Tokens contract
if ((*cpTokens->ismyvin)(tx.vin[i].scriptSig) /*|| IsVinAllowed(tx.vin[i].scriptSig) != 0*/)
{
//std::cerr << indentStr << "TokensExactAmounts() eval is true=" << (eval != NULL) << " ismyvin=ok for_i=" << i << std::endl;
// we are not inside the validation code -- dimxy
if ((eval && eval->GetTxUnconfirmed(tx.vin[i].prevout.hash, vinTx, hashBlock) == 0) || (!eval && !myGetTransaction(tx.vin[i].prevout.hash, vinTx, hashBlock)))
{
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "TokensExactAmounts() cannot read vintx for i." << i << " numvins." << numvins << std::endl);
return (!eval) ? false : eval->Invalid("always should find vin tx, but didnt");
}
else {
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() checking vintx.vout for tx.vin[" << i << "] nValue=" << vinTx.vout[tx.vin[i].prevout.n].nValue << std::endl);
// validate vouts of vintx
tokenValIndentSize++;
tokenoshis = IsTokensvout(goDeeper, true, cpTokens, eval, vinTx, tx.vin[i].prevout.n, reftokenid);
tokenValIndentSize--;
if (tokenoshis != 0)
{
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding vintx.vout for tx.vin[" << i << "] tokenoshis=" << tokenoshis << std::endl);
inputs += tokenoshis;
}
}
}
}
for (int32_t i = 0; i < numvouts-1; i ++) // 'numvouts-1' <-- do not check opret
{
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << indentStr << "TokenExactAmounts() recursively checking tx.vout[" << i << "] nValue=" << tx.vout[i].nValue << std::endl);
// Note: we pass in here IsTokenvout(false,...) because we don't need to call TokenExactAmounts() recursively from IsTokensvout here
// indeed, if we pass 'true' we'll be checking this tx vout again
tokenValIndentSize++;
tokenoshis = IsTokensvout(false /*<--do not recursion here*/, true /*<--exclude non-tokens vouts*/, cpTokens, eval, tx, i, reftokenid);
tokenValIndentSize--;
if (tokenoshis != 0)
{
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokensExactAmounts() adding tx.vout[" << i << "] tokenoshis=" << tokenoshis << std::endl);
outputs += tokenoshis;
}
}
//std::cerr << indentStr << "TokensExactAmounts() inputs=" << inputs << " outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << std::endl;
if (inputs != outputs) {
if (tx.GetHash() != reftokenid)
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "TokenExactAmounts() found unequal token cc inputs=" << inputs << " vs cc outputs=" << outputs << " for txid=" << tx.GetHash().GetHex() << " and this is not the create tx" << std::endl);
//fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
return false; // do not call eval->Invalid() here!
}
else
return true;
}
// get non-fungible data from 'tokenbase' tx (the data might be empty)
void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible)
{
CTransaction tokenbasetx;
uint256 hashBlock;
if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "GetNonfungibleData() cound not load token creation tx=" << tokenid.GetHex() << std::endl);
return;
}
vopretNonfungible.clear();
// check if it is non-fungible tx and get its second evalcode from non-fungible payload
if (tokenbasetx.vout.size() > 0) {
std::vector<uint8_t> origpubkey;
std::string name, description;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') {
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
}
}
}
// overload, adds inputs from token cc addr
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs) {
vscript_t vopretNonfungibleDummy;
return AddTokenCCInputs(cp, mtx, pk, tokenid, total, maxinputs, vopretNonfungibleDummy);
}
// adds inputs from token cc addr and returns non-fungible opret payload if present
// also sets evalcode in cp, if needed
int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey pk, uint256 tokenid, int64_t total, int32_t maxinputs, vscript_t &vopretNonfungible)
{
char tokenaddr[64], destaddr[64];
int64_t threshold, nValue, price, totalinputs = 0;
int32_t n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetNonfungibleData(tokenid, vopretNonfungible);
if (vopretNonfungible.size() > 0)
cp->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
GetTokensCCaddress(cp, tokenaddr, pk);
SetCCunspents(unspentOutputs, tokenaddr,true);
if (unspentOutputs.empty()) {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl);
}
threshold = total / (maxinputs != 0 ? maxinputs : CC_MAXVINS);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
{
CTransaction vintx;
uint256 hashBlock;
uint256 vintxid = it->first.txhash;
int32_t vout = (int32_t)it->first.index;
if (it->second.satoshis < threshold) // this should work also for non-fungible tokens (there should be only 1 satoshi for non-fungible token issue)
continue;
int32_t ivin;
for (ivin = 0; ivin < mtx.vin.size(); ivin ++)
if (vintxid == mtx.vin[ivin].prevout.hash && vout == mtx.vin[ivin].prevout.n)
break;
if (ivin != mtx.vin.size()) // that is, the tx.vout is already added to mtx.vin (in some previous calls)
continue;
if (GetTransaction(vintxid, vintx, hashBlock, false) != 0)
{
Getscriptaddress(destaddr, vintx.vout[vout].scriptPubKey);
if (strcmp(destaddr, tokenaddr) != 0 &&
strcmp(destaddr, cp->unspendableCCaddr) != 0 && // TODO: check why this. Should not we add token inputs from unspendable cc addr if mypubkey is used?
strcmp(destaddr, cp->unspendableaddr2) != 0) // or the logic is to allow to spend all available tokens (what about unspendableaddr3)?
continue;
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() check vintx vout destaddress=" << destaddr << " amount=" << vintx.vout[vout].nValue << std::endl);
if ((nValue = IsTokensvout(true, true/*<--add only valid token uxtos */, cp, NULL, vintx, vout, tokenid)) > 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,vintxid, vout) == 0)
{
//for non-fungible tokens check payload:
if (!vopretNonfungible.empty()) {
vscript_t vopret;
// check if it is non-fungible token:
GetNonfungibleData(tokenid, vopret);
if (vopret != vopretNonfungible) {
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() found incorrect non-fungible opret payload for vintxid=" << vintxid.GetHex() << std::endl);
continue;
}
// non-fungible evalCode2 cc contract should also check if there exists only one non-fungible vout with amount = 1
}
if (total != 0 && maxinputs != 0) // if it is not just to calc amount...
mtx.vin.push_back(CTxIn(vintxid, vout, CScript()));
nValue = it->second.satoshis;
totalinputs += nValue;
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "AddTokenCCInputs() adding input nValue=" << nValue << std::endl);
n++;
if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs))
break;
}
}
}
//std::cerr << "AddTokenCCInputs() found totalinputs=" << totalinputs << std::endl;
return(totalinputs);
}
// checks if any token vouts are sent to 'dead' pubkey
int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid)
{
uint8_t dummyEvalCode;
uint256 tokenIdOpret;
std::vector<CPubKey> voutPubkeys, voutPubkeysDummy;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
vscript_t vopretExtra, vopretNonfungible;
uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim
uint8_t evalCode2 = 0; // will be checked if zero or not
// test vouts for possible token use-cases:
std::vector<std::pair<CTxOut, std::string>> 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<CPubKey>::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<CPubKey> voutTokenPubkeys;
std::vector<std::pair<uint8_t, vscript_t>> 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 (tokensupply < 0) {
CCerror = "negative tokensupply";
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() =" << CCerror << "=" << tokensupply << std::endl);
return std::string("");
}
if (!nonfungibleData.empty() && tokensupply != 1) {
CCerror = "for non-fungible tokens tokensupply should be equal to 1";
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl);
return std::string("");
}
cp = CCinit(&C, EVAL_TOKENS);
if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level
{
LOGSTREAM((char *)"cctokens", CCLOG_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, tokensupply + 2 * txfee, 64) > 0)
{
uint8_t destEvalCode = EVAL_TOKENS;
if( nonfungibleData.size() > 0 )
destEvalCode = nonfungibleData.begin()[0];
// NOTE: we should prevent spending fake-tokens from this marker in IsTokenvout():
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // new marker to token cc addr, burnable and validated, vout pos now changed to 0 (from 1)
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, tokensupply, mypk));
//mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(cp->CChexstr) << OP_CHECKSIG)); // old marker (non-burnable because spending could not be validated)
//mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cp, NULL))); // ...moved to vout=0 for matching with rogue-game token
return(FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeTokenCreateOpRet('c', Mypubkey(), name, description, nonfungibleData)));
}
CCerror = "cant find normal inputs";
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "CreateToken() " << CCerror << std::endl);
return std::string("");
}
// transfer tokens to another pubkey
// param additionalEvalCode allows transfer of dual-eval non-fungible tokens
std::string TokenTransfer(int64_t txfee, uint256 tokenid, vscript_t destpubkey, int64_t total)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint64_t mask; int64_t CCchange = 0, inputs = 0; struct CCcontract_info *cp, C;
vscript_t vopretNonfungible, vopretEmpty;
if (total < 0) {
CCerror = strprintf("negative total");
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << CCerror << "=" << total << std::endl);
return("");
}
cp = CCinit(&C, EVAL_TOKENS);
if (txfee == 0)
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
if (AddNormalinputs(mtx, mypk, txfee, 3) > 0)
{
mask = ~((1LL << mtx.vin.size()) - 1); // seems, mask is not used anymore
if ((inputs = AddTokenCCInputs(cp, mtx, mypk, tokenid, total, 60, vopretNonfungible)) > 0) // NOTE: AddTokenCCInputs might set cp->additionalEvalCode which is used in FinalizeCCtx!
{
if (inputs < total) { //added dimxy
CCerror = strprintf("insufficient token inputs");
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl);
return std::string("");
}
uint8_t destEvalCode = EVAL_TOKENS;
if (vopretNonfungible.size() > 0)
destEvalCode = vopretNonfungible.begin()[0];
if (inputs > total)
CCchange = (inputs - total);
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, total, pubkey2pk(destpubkey))); // if destEvalCode == EVAL_TOKENS then it is actually MakeCC1vout(EVAL_TOKENS,...)
if (CCchange != 0)
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, CCchange, mypk));
std::vector<CPubKey> voutTokenPubkeys;
voutTokenPubkeys.push_back(pubkey2pk(destpubkey)); // dest pubkey for validating vout
return FinalizeCCTx(mask, cp, mtx, mypk, txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair((uint8_t)0, vopretEmpty)));
}
else {
CCerror = strprintf("no token inputs");
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << total << std::endl);
}
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
}
else {
CCerror = strprintf("insufficient normal inputs for tx fee");
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << std::endl);
}
return("");
}
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid)
{
uint256 hashBlock;
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CTransaction tokentx;
// CCerror = strprintf("obsolete, cannot return correct value without eval");
// return 0;
if (GetTransaction(tokenid, tokentx, hashBlock, false) == 0)
{
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "cant find tokenid" << std::endl);
CCerror = strprintf("cant find tokenid");
return 0;
}
struct CCcontract_info *cp, C;
cp = CCinit(&C, EVAL_TOKENS);
return(AddTokenCCInputs(cp, mtx, pk, tokenid, 0, 0));
}
UniValue TokenInfo(uint256 tokenid)
{
UniValue result(UniValue::VOBJ);
uint256 hashBlock;
CTransaction vintx;
std::vector<uint8_t> origpubkey;
std::vector<std::pair<uint8_t, vscript_t>> 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, "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, oprets) != 'c')
{
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl);
result.push_back(Pair("result", "error"));
result.push_back(Pair("error", "tokenid isnt token creation txid"));
return result;
}
result.push_back(Pair("result", "success"));
result.push_back(Pair("tokenid", tokenid.GetHex()));
result.push_back(Pair("owner", HexStr(origpubkey)));
result.push_back(Pair("name", name));
int64_t supply = 0, output;
for (int v = 0; v < 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));
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<std::pair<CAddressIndexKey, CAmount> > addressIndex;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > addressIndexCCMarker;
struct CCcontract_info *cp, C; uint256 txid, hashBlock;
CTransaction vintx; std::vector<uint8_t> origpubkey;
std::string name, description;
cp = CCinit(&C, EVAL_TOKENS);
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<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it = addressIndexCCMarker.begin(); it != addressIndexCCMarker.end(); it++) {
addTokenId(it->first.txhash);
}
return(result);
}

40
src/cc/CCtokens.h Normal file
View File

@@ -0,0 +1,40 @@
/******************************************************************************
* 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. *
* *
******************************************************************************/
/*
CCassetstx has the functions that create the EVAL_ASSETS transactions. It is expected that rpc calls would call these functions. For EVAL_ASSETS, the rpc functions are in rpcwallet.cpp
CCassetsCore has functions that are used in two contexts, both during rpc transaction create time and also during the blockchain validation. Using the identical functions is a good way to prevent them from being mismatched. The must match or the transaction will get rejected.
*/
#ifndef CC_TOKENS_H
#define CC_TOKENS_H
#include "CCinclude.h"
// CCcustom
bool TokensValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn);
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid);
std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, std::string description, std::vector<uint8_t> nonfungibleData);
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> 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();
#endif

296
src/cc/CCtokensOpRet.cpp Normal file
View File

@@ -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<uint8_t> 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<std::pair<uint8_t, vscript_t>> 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<uint8_t> origpubkey, std::string name, std::string description, std::vector<std::pair<uint8_t, vscript_t>> 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<uint8_t> origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector<std::pair<uint8_t, vscript_t>> 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<CPubKey> voutPubkeys, std::pair<uint8_t, vscript_t> opretWithId)
{
std::vector<std::pair<uint8_t, vscript_t>> oprets;
oprets.push_back(opretWithId);
return EncodeTokenOpRet(tokenid, voutPubkeys, oprets);
}
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> 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<CPubKey> 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<uint8_t> &origpubkey, std::string &name, std::string &description) {
//vopret_t vopretNonfungibleDummy;
std::vector<std::pair<uint8_t, vscript_t>> opretsDummy;
return DecodeTokenCreateOpRet(scriptPubKey, origpubkey, name, description, opretsDummy);
}
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, std::vector<std::pair<uint8_t, vscript_t>> &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<uint8_t> &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector<std::pair<uint8_t, vscript_t>> &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<CPubKey> &voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> &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;
}

View File

@@ -16,6 +16,8 @@
#include "CCinclude.h"
#include "key_io.h"
std::vector<CPubKey> NULL_pubkeys;
/*
FinalizeCCTx is a very useful function that will properly sign both CC and normal inputs, adds normal change and the opreturn.
@@ -38,13 +40,18 @@ bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScrip
return(false);
}
std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret)
std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret,std::vector<CPubKey> pubkeys)
{
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
CTransaction vintx; std::string hex; uint256 hashBlock; uint64_t mask=0,nmask=0,vinimask=0;
int64_t utxovalues[64],change,normalinputs=0,totaloutputs=0,normaloutputs=0,totalinputs=0,normalvins=0,ccvins=0;
int32_t i,utxovout,n,err = 0; char myaddr[64],destaddr[64],unspendable[64];
uint8_t *privkey,myprivkey[32],unspendablepriv[32],*msg32 = 0; CC *mycond=0,*othercond=0,*othercond2=0,*othercond3=0,*cond; CPubKey unspendablepk;
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], 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);
n = mtx.vout.size();
for (i=0; i<n; i++)
{
@@ -52,17 +59,39 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
normaloutputs += mtx.vout[i].nValue;
totaloutputs += mtx.vout[i].nValue;
}
if ( (n= mtx.vin.size()) > 64 )
if ( (n= mtx.vin.size()) > CC_MAXVINS )
{
fprintf(stderr,"FinalizeCCTx: %d is too many vins\n",n);
return("0");
}
Myprivkey(myprivkey);
unspendablepk = GetUnspendable(cp,unspendablepriv);
GetCCaddress(cp,myaddr,mypk);
mycond = MakeCCcond1(cp->evalcode,mypk);
GetCCaddress(cp,unspendable,unspendablepk);
othercond = MakeCCcond1(cp->evalcode,unspendablepk);
// 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/three-eval mypk vout
GetTokensCCaddress(cp, mytokensaddr, mypk);
// NOTE: if additionalEvalcode2 is not set it is a dual-eval (not three-eval) cc cond:
mytokenscond = MakeTokensCCcond1(cp->evalcode, cp->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/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.
for (i=0; i<n; i++)
@@ -127,41 +156,107 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
else
{
Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey);
//fprintf(stderr,"vin.%d is CC %.8f -> (%s)\n",i,(double)utxovalues[i]/COIN,destaddr);
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;
}
else if (strcmp(destaddr, mytokensaddr) == 0) // if this is TokensCC1vout
{
privkey = myprivkey;
cond = mytokenscond;
//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 my token addr.(%s)\n", mytokensaddr);
}
else if ( strcmp(destaddr,unspendable) == 0 )
{
privkey = unspendablepriv;
cond = othercond;
//fprintf(stderr,"unspendable CC addr.(%s)\n",unspendable);
//fprintf(stderr,"FinalizeCCTx evalcode(%d) matched unspendable CC addr.(%s)\n",cp->evalcode,unspendable);
}
else if ( strcmp(destaddr,cp->unspendableaddr2) == 0)
else if (strcmp(destaddr, unspendabletokensaddr) == 0)
{
privkey = unspendablepriv;
cond = othertokenscond;
//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)
{
//fprintf(stderr,"matched %s unspendable2!\n",cp->unspendableaddr2);
//fprintf(stderr,"FinalizeCCTx() matched %s unspendable2!\n",cp->unspendableaddr2);
privkey = cp->unspendablepriv2;
if ( othercond2 == 0 && cp->evalcode != EVAL_CHANNELS && cp->evalcode != EVAL_HEIR )
othercond2 = MakeCCcond1(cp->evalcode2,cp->unspendablepk2);
else if ( othercond2 == 0 && (cp->evalcode == EVAL_CHANNELS || cp->evalcode == EVAL_HEIR) )
othercond2 = MakeCCcond1of2(cp->evalcode2,cp->unspendablepk2,cp->unspendablepk3);
if( othercond2 == 0 )
othercond2 = MakeCCcond1(cp->unspendableEvalcode2, cp->unspendablepk2);
cond = othercond2;
}
// check if this is 3rd additional evalcode + 'unspendable' cc addr:
else if ( strcmp(destaddr,cp->unspendableaddr3) == 0 )
{
//fprintf(stderr,"matched %s unspendable3!\n",cp->unspendableaddr3);
//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 = 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)
// 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
{
fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr);
continue;
flag = 0;
if ( pubkeys != NULL_pubkeys )
{
char coinaddr[64];
GetCCaddress1of2(cp,coinaddr,globalpk,pubkeys[i]);
//fprintf(stderr,"%s + %s -> %s vs %s\n",HexStr(globalpk).c_str(),HexStr(pubkeys[i]).c_str(),coinaddr,destaddr);
if ( strcmp(destaddr,coinaddr) == 0 )
{
privkey = cp->CCpriv;
if ( othercond4 != 0 )
cc_free(othercond4);
othercond4 = MakeCCcond1of2(cp->evalcode,globalpk,pubkeys[i]);
cond = othercond4;
flag = 1;
}
}
if ( flag == 0 )
{
fprintf(stderr,"CC signing error: vini.%d has unknown CC address.(%s)\n",i,destaddr);
return("");
}
}
uint256 sighash = SignatureHash(CCPubKey(cond), mtx, i, SIGHASH_ALL, utxovalues[i],consensusBranchId, &txdata);
if ( cc_signTreeSecp256k1Msg32(cond,privkey,sighash.begin()) != 0 )
@@ -178,25 +273,40 @@ std::string FinalizeCCTx(uint64_t CCmask,struct CCcontract_info *cp,CMutableTran
else
{
fprintf(stderr,"vini.%d has CC signing error address.(%s)\n",i,destaddr);
return("");
}
}
} 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 )
cc_free(othercond2);
if ( othercond3 != 0 )
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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr)
void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr,bool ccflag)
{
int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector<std::pair<uint160, int> > addresses;
n = (int32_t)strlen(coinaddr);
@@ -205,7 +315,7 @@ void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValu
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<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++)
@@ -215,7 +325,7 @@ void SetCCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValu
}
}
void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,char *coinaddr)
void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &addressIndex,char *coinaddr,bool ccflag)
{
int32_t type=0,i,n; char *ptr; std::string addrstr; uint160 hashBytes; std::vector<std::pair<uint160, int> > addresses;
n = (int32_t)strlen(coinaddr);
@@ -224,7 +334,7 @@ void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &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<std::pair<uint160, int> >::iterator it = addresses.begin(); it != addresses.end(); it++)
@@ -234,10 +344,10 @@ void SetCCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;
@@ -247,10 +357,56 @@ int64_t CCutxovalue(char *coinaddr,uint256 utxotxid,int32_t utxovout)
return(0);
}
int64_t CCaddress_balance(char *coinaddr)
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 )
{
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
{
if (!pcoinsTip->GetCoins(txid, coins))
return(-1);
}
if ( vout < coins.vout.size() )
return(coins.vout[vout].nValue);
else return(-1);
}
int32_t CCgetspenttxid(uint256 &spenttxid,int32_t &vini,int32_t &height,uint256 txid,int32_t vout)
{
CSpentIndexKey key(txid, vout);
CSpentIndexValue value;
if ( !GetSpentIndex(key, value) )
return(-1);
spenttxid = value.txid;
vini = (int32_t)value.inputIndex;
height = value.blockHeight;
return(0);
}
int64_t CCaddress_balance(char *coinaddr,int32_t CCflag)
{
int64_t sum = 0; std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,CCflag!=0?true:false);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
sum += it->second.satoshis;
@@ -261,28 +417,33 @@ int64_t CCaddress_balance(char *coinaddr)
int64_t CCfullsupply(uint256 tokenid)
{
uint256 hashBlock; int32_t numvouts; CTransaction tx; std::vector<uint8_t> 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 ( DecodeAssetCreateOpRet(tx.vout[numvouts-1].scriptPubKey,origpubkey,name,description) > 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; std::vector<uint8_t> origpubkey;
int64_t price,sum = 0; int32_t numvouts; CTransaction tx; uint256 tokenid,txid,hashBlock;
std::vector<uint8_t> vopretExtra;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
SetCCunspents(unspentOutputs,coinaddr);
uint8_t evalCode;
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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);
if ( DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,assetid2,price,origpubkey) != 0 && assetid == tokenid )
char str[65];
std::vector<CPubKey> voutTokenPubkeys;
std::vector<std::pair<uint8_t, vscript_t>> oprets;
if ( reftokenid==txid || (DecodeTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCode, tokenid, voutTokenPubkeys, oprets) != 0 && reftokenid == tokenid))
{
sum += it->second.satoshis;
}
@@ -348,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<COutput> 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<COutput> vecOutputs; CTransaction tx; struct CC_utxo *utxos,*up;
#ifdef ENABLE_WALLET
const CKeyStore& keystore = *pwalletMain;
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)
{
@@ -384,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;
@@ -392,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;
}
}
@@ -438,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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;
@@ -475,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;
@@ -483,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;
}
}

105
src/cc/CCutilbits.cpp Normal file
View File

@@ -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<unsigned char> 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<uint8_t> 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<n; i++)
dest[i] = pubkey33[i];
return(pk);
}

View File

@@ -58,24 +58,114 @@ CC *MakeCCcond1(uint8_t evalcode,CPubKey pk)
return CCNewThreshold(2, {condCC, Sig});
}
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk)
CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue, CPubKey pk, const std::vector<std::vector<unsigned char>>* vData)
{
CTxOut vout;
CC *payoutCond = MakeCCcond1(evalcode,pk);
vout = CTxOut(nValue,CCPubKey(payoutCond));
if ( vData )
{
std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
std::vector<CPubKey> vPubKeys = std::vector<CPubKey>();
vPubKeys.push_back(pk);
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 1, vPubKeys, vtmpData);
vout.scriptPubKey << ccp.AsVector() << OP_DROP;
}
cc_free(payoutCond);
return(vout);
}
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2)
CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, const std::vector<std::vector<unsigned char>>* vData)
{
CTxOut vout;
CC *payoutCond = MakeCCcond1of2(evalcode,pk1,pk2);
vout = CTxOut(nValue,CCPubKey(payoutCond));
if ( vData )
{
std::vector<std::vector<unsigned char>> vtmpData = std::vector<std::vector<unsigned char>>(vData->begin(), vData->end());
std::vector<CPubKey> vPubKeys = std::vector<CPubKey>();
vPubKeys.push_back(pk1);
vPubKeys.push_back(pk2);
COptCCParams ccp = COptCCParams(COptCCParams::VERSION, evalcode, 1, 2, vPubKeys, vtmpData);
vout.scriptPubKey << ccp.AsVector() << OP_DROP;
}
cc_free(payoutCond);
return(vout);
}
// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition:
CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2)
{
// make 1of2 sigs cond
std::vector<CC*> pks;
pks.push_back(CCNewSecp256k1(pk1));
pks.push_back(CCNewSecp256k1(pk2));
std::vector<CC*> thresholds;
thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) );
if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()!
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
if( evalcode2 != 0 )
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc
return CCNewThreshold(thresholds.size(), thresholds);
}
// overload to make two-eval (token+evalcode) 1of2 cryptocondition:
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) {
return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2);
}
// make three-eval (token+evalcode+evalcode2) cryptocondition:
CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
{
std::vector<CC*> pks;
pks.push_back(CCNewSecp256k1(pk));
std::vector<CC*> thresholds;
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()!
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
if (evalcode2 != 0)
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
thresholds.push_back(CCNewThreshold(1, pks)); // signature
return CCNewThreshold(thresholds.size(), thresholds);
}
// overload to make two-eval (token+evalcode) cryptocondition:
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) {
return MakeTokensCCcond1(evalcode, 0, pk);
}
// make three-eval (token+evalcode+evalcode2) 1of2 cc vout:
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2)
{
CTxOut vout;
CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2);
vout = CTxOut(nValue, CCPubKey(payoutCond));
cc_free(payoutCond);
return(vout);
}
// overload to make two-eval (token+evalcode) 1of2 cc vout:
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) {
return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2);
}
// make three-eval (token+evalcode+evalcode2) cc vout:
CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk)
{
CTxOut vout;
CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk);
vout = CTxOut(nValue, CCPubKey(payoutCond));
cc_free(payoutCond);
return(vout);
}
// overload to make two-eval (token+evalcode) cc vout:
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) {
return MakeTokensCC1vout(evalcode, 0, nValue, pk);
}
CC* GetCryptoCondition(CScript const& scriptSig)
{
auto pc = scriptSig.begin();
@@ -95,106 +185,55 @@ 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<unsigned char> 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<uint8_t> 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<n; i++)
dest[i] = pubkey33[i];
return(pk);
return true;
}
// set additional 'unspendable' addr
void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr)
{
cp->evalcode2 = 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,uint8_t *priv,char *coinaddr)
{
cp->coins1of2pk[0] = pk1;
cp->coins1of2pk[1] = pk2;
memcpy(cp->coins1of2priv,priv,32);
strcpy(cp->coins1of2addr,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, tokenaddr);
}
bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey)
{
CTxDestination address; txnouttype whichType;
@@ -207,6 +246,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<std::vector<unsigned char>> &preConditions, std::vector<std::vector<unsigned char>> &params)
{
@@ -267,6 +318,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;
@@ -287,6 +348,28 @@ bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk)
return(_GetCCaddress(destaddr,cp->evalcode,pk));
}
bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
{
CC *payoutCond;
destaddr[0] = 0;
if ((payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk)) != 0)
{
Getscriptaddress(destaddr, CCPubKey(payoutCond));
cc_free(payoutCond);
}
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, cp->additionalTokensEvalcode2, pk));
}
bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2)
{
CC *payoutCond;
@@ -299,17 +382,30 @@ bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubK
return(destaddr[0] != 0);
}
bool ConstrainVout(CTxOut vout,int32_t CCflag,char *cmpaddr,int64_t nValue)
// 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, 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);
}
return(destaddr[0] != 0);
}
bool ConstrainVout(CTxOut vout, int32_t CCflag, char *cmpaddr, int64_t nValue)
{
char destaddr[64];
if ( vout.scriptPubKey.IsPayToCryptoCondition() != CCflag )
{
fprintf(stderr,"constrain vout error isCC %d vs %d CCflag\n",vout.scriptPubKey.IsPayToCryptoCondition(),CCflag);
fprintf(stderr,"constrain vout error isCC %d vs %d CCflag\n", vout.scriptPubKey.IsPayToCryptoCondition(), CCflag);
return(false);
}
else if ( cmpaddr != 0 && (Getscriptaddress(destaddr,vout.scriptPubKey) == 0 || strcmp(destaddr,cmpaddr) != 0) )
else if ( cmpaddr != 0 && (Getscriptaddress(destaddr, vout.scriptPubKey) == 0 || strcmp(destaddr, cmpaddr) != 0) )
{
fprintf(stderr,"constrain vout error addr %s vs %s\n",cmpaddr!=0?cmpaddr:"",destaddr!=0?destaddr:"");
fprintf(stderr,"constrain vout error: check addr %s vs script addr %s\n", cmpaddr!=0?cmpaddr:"", destaddr!=0?destaddr:"");
return(false);
}
else if ( nValue != 0 && nValue != vout.nValue ) //(nValue == 0 && vout.nValue < 10000) || (
@@ -345,6 +441,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<uint8_t> Mypubkey()
{
extern uint8_t NOTARY_PUBKEY33[33];
@@ -359,7 +469,7 @@ std::vector<uint8_t> 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);
@@ -380,7 +490,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
}
@@ -396,39 +512,10 @@ CPubKey GetUnspendable(struct CCcontract_info *cp,uint8_t *unspendablepriv)
return(pubkey2pk(ParseHex(cp->CChexstr)));
}
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
void CCclearvars(struct CCcontract_info *cp)
{
CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector<uint8_t> origpubkey;
height = KOMODO_CONNECTING;
if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation
return(true);
if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE )
return eval->Invalid("CC are disabled or not active yet");
if ( (KOMODO_CONNECTING & (1<<30)) != 0 )
{
from_mempool = 1;
height &= ((1<<30) - 1);
}
//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();
//if ( txid == cp->prevtxid )
// return(true);
//fprintf(stderr,"process CC %02x\n",cp->evalcode);
cp->evalcode2 = cp->evalcode3 = 0;
cp->unspendableEvalcode2 = cp->unspendableEvalcode3 = 0;
cp->unspendableaddr2[0] = cp->unspendableaddr3[0] = 0;
if ( paramsNull.size() != 0 ) // Don't expect params
return eval->Invalid("Cannot have params");
//else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses
// return eval->Invalid("no-vouts");
else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 )
{
//fprintf(stderr,"done CC %02x\n",cp->evalcode);
//cp->prevtxid = txid;
return(true);
}
//fprintf(stderr,"invalid CC %02x\n",cp->evalcode);
return(false);
}
int64_t CCduration(int32_t &numblocks,uint256 txid)
@@ -462,6 +549,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::vector<uint8_t>data; 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<n; i++)
{
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
if ( strcmp(destaddr,coinaddr) == 0 )
{
LogPrint(logcategory,"found (%s) vout in mempool\n",coinaddr);
return(1);
}
}
}
}
return(0);
}
int32_t CCCointxidExists(char const *logcategory,uint256 cointxid)
{
char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock;
std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
CCtxidaddr(txidaddr,cointxid);
SetCCtxids(addressIndex,txidaddr,true);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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<uint8_t> &proofData, std::vector<uint256> &txids)
{
CMerkleBlock merkleBlock;
if (!E_UNMARSHAL(proofData, ss >> merkleBlock))
return uint256();
return merkleBlock.txn.ExtractMatches(txids);
}
bool komodo_txnotarizedconfirmed(uint256 txid)
{
char str[65];
@@ -502,3 +678,112 @@ bool komodo_txnotarizedconfirmed(uint256 txid)
return (true);
return (false);
}
CPubKey check_signing_pubkey(CScript scriptSig)
{
bool found = false;
CPubKey pubkey;
auto findEval = [](CC *cond, struct CCVisitor _) {
bool r = false;
if (cc_typeId(cond) == CC_Secp256k1) {
*(CPubKey*)_.context=buf2pk(cond->publicKey);
r = true;
}
// false for a match, true for continue
return r ? 0 : 1;
};
CC *cond = GetCryptoCondition(scriptSig);
if (cond) {
CCVisitor visitor = { findEval, (uint8_t*)"", 0, &pubkey };
bool out = !cc_visit(cond, visitor);
cc_free(cond);
if (pubkey.IsValid()) {
return pubkey;
}
}
return CPubKey();
}
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
{
CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector<uint8_t> origpubkey;
height = KOMODO_CONNECTING;
if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation
return(true);
if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE )
return eval->Invalid("CC are disabled or not active yet");
if ( (KOMODO_CONNECTING & (1<<30)) != 0 )
{
from_mempool = 1;
height &= ((1<<30) - 1);
}
if (cp->validate == NULL)
return eval->Invalid("validation not supported for eval code");
//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();
//if ( txid == cp->prevtxid )
// return(true);
//fprintf(stderr,"process CC %02x\n",cp->evalcode);
CCclearvars(cp);
if ( paramsNull.size() != 0 ) // Don't expect params
return eval->Invalid("Cannot have params");
//else if ( ctx.vout.size() == 0 ) // spend can go to z-addresses
// return eval->Invalid("no-vouts");
else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 )
{
//fprintf(stderr,"done CC %02x\n",cp->evalcode);
//cp->prevtxid = txid;
return(true);
}
//fprintf(stderr,"invalid CC %02x\n",cp->evalcode);
return(false);
}
extern struct CCcontract_info CCinfos[0x100];
extern std::string MYCCLIBNAME;
bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn);
bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector<uint8_t> paramsNull,const CTransaction &txTo,unsigned int nIn)
{
uint8_t evalcode; int32_t height,from_mempool; struct CCcontract_info *cp;
if ( ASSETCHAINS_CCLIB != MYCCLIBNAME )
{
fprintf(stderr,"-ac_cclib=%s vs myname %s\n",ASSETCHAINS_CCLIB.c_str(),MYCCLIBNAME.c_str());
return eval->Invalid("-ac_cclib name mismatches myname");
}
height = KOMODO_CONNECTING;
if ( KOMODO_CONNECTING < 0 ) // always comes back with > 0 for final confirmation
return(true);
if ( ASSETCHAINS_CC == 0 || (height & ~(1<<30)) < KOMODO_CCACTIVATE )
return eval->Invalid("CC are disabled or not active yet");
if ( (KOMODO_CONNECTING & (1<<30)) != 0 )
{
from_mempool = 1;
height &= ((1<<30) - 1);
}
evalcode = cond->code[0];
if ( evalcode >= EVAL_FIRSTUSER && evalcode <= EVAL_LASTUSER )
{
cp = &CCinfos[(int32_t)evalcode];
if ( cp->didinit == 0 )
{
if ( CClib_initcp(cp,evalcode) == 0 )
cp->didinit = 1;
else return eval->Invalid("unsupported CClib evalcode");
}
CCclearvars(cp);
if ( paramsNull.size() != 0 ) // Don't expect params
return eval->Invalid("Cannot have params");
else if ( CClib_validate(cp,height,eval,txTo,nIn) != 0 )
return(true);
return(false); //eval->Invalid("error in CClib_validate");
}
return eval->Invalid("cclib CC must have evalcode between 16 and 127");
}

34
src/cc/Makefile Normal file
View File

@@ -0,0 +1,34 @@
SHELL = /bin/sh
CC = gcc
CC_DARWIN = g++-6
CC_WIN = x86_64-w64-mingw32-gcc-posix
CFLAGS_DARWIN = -std=c++11 -arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -c -Wl,-undefined -Wl,dynamic_lookup -dynamiclib
CFLAGS = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c
CFLAGS_WIN = -std=c++11 -I../../depends/$(shell echo `../..//depends/config.guess`/include) -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c
DEBUGFLAGS = -O0 -D _DEBUG
RELEASEFLAGS = -O2 -D NDEBUG -combine -fwhole-program
$(info $(OS))
OS := $(shell uname -s)
$(info $(OS))
TARGET = ../libcc.so
TARGET_DARWIN = ../libcc.dylib
TARGET_WIN = ../libcc.dll
SOURCES = cclib.cpp
#HEADERS = $(shell echo ../cryptoconditions/include/*.h)
all: $(TARGET)
$(TARGET): $(SOURCES)
$(info Building cclib to src/)
ifeq ($(OS),Darwin)
$(CC_DARWIN) $(CFLAGS_DARWIN) $(DEBUGFLAGS) -o $(TARGET_DARWIN) $(SOURCES)
else ifeq ($(OS),Linux)
$(CC) $(CFLAGS) $(DEBUGFLAGS) -o $(TARGET) $(SOURCES)
#else ifeq ($(WIN_HOST),True) - todo: pass ENV var from build.sh if WIN host
else
$(info WINDOWS)
$(CC_WIN) $(CFLAGS_WIN) $(DEBUGFLAGS) -o $(TARGET_WIN) $(SOURCES)
endif
clean:
rm -rf $(TARGET)

38
src/cc/Makefile_rogue Normal file
View File

@@ -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)

7
src/cc/README.md Normal file
View File

@@ -0,0 +1,7 @@
## CCLIB
Please follow the below instructions to build the cryptoconditions library into the Komodo source directory `komodo/src` - supported operating systems are Linux, OSX and Windows (mingw crossbuild):
```
make clean
make
```

View File

@@ -14,6 +14,7 @@
******************************************************************************/
#include "CCassets.h"
#include "CCtokens.h"
/*
Assets can be created or transferred.
@@ -45,32 +46,22 @@
valid CC output: create or transfer or buyoffer or selloffer or exchange or cancel or fill
create
vin.0: normal input
vout.0: issuance assetoshis to CC
vout.1: tag sent to normal address of AssetsCCaddress
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['c'] [origpubkey] "<assetname>" "<description>"
transfer
vin.0: normal input
vin.1 .. vin.n-1: valid CC outputs
vout.0 to n-2: assetoshis output to CC
vout.n-2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
buyoffer:
vins.*: normal inputs (bid + change)
vout.0: amount of bid to unspendable
vout.1: normal output for change (if any)
vout.1: CC output for marker
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['b'] [assetid] [amount of asset required] [origpubkey]
cancelbuy:
vin.0: normal input
vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
vin.2: CC marker from buyoffer for txfee
vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
vout.1: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid]
vout.1: vin.2 back to users pubkey
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['o'] [assetid] 0 0 [origpubkey]
fillbuy:
vin.0: normal input
@@ -87,8 +78,9 @@
vin.0: normal input
vin.1+: valid CC output for sale
vout.0: vin.1 assetoshis output to CC to unspendable
vout.1: CC output for change (if any)
vout.2: normal output for change (if any)
vout.1: CC output for marker
vout.2: CC output for change (if any)
vout.3: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
exchange:
@@ -102,8 +94,10 @@
cancel:
vin.0: normal input
vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
vin.2: CC marker from selloffer for txfee
vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
vout.1: normal output for change (if any)
vout.1: vin.2 back to users pubkey
vout.2: normal output for change (if any)
vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
fillsell:
@@ -133,45 +127,99 @@
// tx validation
bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
bool AssetsValidate(struct CCcontract_info *cpAssets,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
static uint256 zero;
CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2; int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector<uint8_t> origpubkey,tmporigpubkey,ignorepubkey; uint8_t funcid; char destaddr[64],origaddr[64],CCaddr[64];
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<uint8_t> origpubkey, tmporigpubkey, ignorepubkey, vopretNonfungible, vopretNonfungibleDummy;
uint8_t funcid, evalCodeInOpret;
char destaddr[64], origNormalAddr[64], origTokensCCaddr[64], origCCaddrDummy[64];
char tokensDualEvalUnspendableCCaddr[64], origAssetsCCaddr[64];
//return true;
numvins = tx.vin.size();
numvouts = tx.vout.size();
outputs = inputs = 0;
outputsDummy = inputs = 0;
preventCCvins = preventCCvouts = -1;
if ( (funcid= DecodeAssetOpRet(tx.vout[numvouts-1].scriptPubKey,assetid,assetid2,remaining_price,origpubkey)) == 0 )
return eval->Invalid("Invalid opreturn payload");
fprintf(stderr,"AssetValidate (%c)\n",funcid);
if ( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid,createTx,hashBlock) == 0 )
// add specific chains exceptions for old token support:
if (strcmp(ASSETCHAINS_SYMBOL, "SEC") == 0 && chainActive.Height() <= 144073)
return true;
if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190)
return true;
// add specific chains exceptions for old token support:
if (strcmp(ASSETCHAINS_SYMBOL, "SEC") == 0 && chainActive.Height() <= 144073)
return true;
if (strcmp(ASSETCHAINS_SYMBOL, "MGNX") == 0 && chainActive.Height() <= 210190)
return true;
if (numvouts == 0)
return eval->Invalid("AssetValidate: no vouts");
if((funcid = DecodeAssetTokenOpRet(tx.vout[numvouts-1].scriptPubKey, evalCodeInOpret, assetid, assetid2, remaining_price, origpubkey)) == 0 )
return eval->Invalid("AssetValidate: invalid opreturn payload");
// non-fungible tokens support:
GetNonfungibleData(assetid, vopretNonfungible);
if (vopretNonfungible.size() > 0)
cpAssets->additionalTokensEvalcode2 = vopretNonfungible.begin()[0];
// find dual-eval tokens unspendable addr:
GetTokensCCaddress(cpAssets, tokensDualEvalUnspendableCCaddr, GetUnspendable(cpAssets, NULL));
// this is for marker validation:
GetCCaddress(cpAssets, origAssetsCCaddr, origpubkey);
// we need this for validating single-eval tokens' vins/vous:
struct CCcontract_info *cpTokens, tokensC;
cpTokens = CCinit(&tokensC, EVAL_TOKENS);
// find single-eval token user cc addr:
//GetCCaddress(cpTokens, signleEvalTokensCCaddr, pubkey2pk(origpubkey));
//fprintf(stderr,"AssetValidate (%c)\n",funcid);
if( funcid != 'o' && funcid != 'x' && eval->GetTxUnconfirmed(assetid, createTx, hashBlock) == 0 )
return eval->Invalid("cant find asset create txid");
else if ( funcid != 'o' && funcid != 'x' && assetid2 != zero && eval->GetTxUnconfirmed(assetid2,createTx,hashBlock) == 0 )
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 < 1 )
return eval->Invalid("no vouts");
else if ( funcid != 'c' )
else if( numvouts < 2 )
return eval->Invalid("too few vouts"); // it was if(numvouts < 1) but it refers at least to vout[1] below
else if( funcid != 'c' )
{
if ( funcid == 't' )
/* if( funcid == 't' )
starti = 0;
else starti = 1;
if ( assetid == zero )
else
starti = 1; */
if( assetid == zero )
return eval->Invalid("illegal assetid");
else if ( AssetExactAmounts(2, cp,inputs,starti,outputs,eval,tx,assetid) == false )
return eval->Invalid("asset inputs != 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
}
}
switch ( funcid )
switch( funcid )
{
case 'c': // create wont be called to be verified as it has no CC inputs
//vin.0: normal input
//vout.0: issuance assetoshis to CC
//vout.1: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['c'] [{"<assetname>":"<description>"}]
return eval->Invalid("unexpected AssetValidate for createasset");
//if (evalCodeInOpret == EVAL_ASSETS)
// return eval->Invalid("unexpected AssetValidate for createasset");
// return
return eval->Invalid("invalid asset funcid \'c\'");
break;
case 't': // transfer
//vin.0: normal input
@@ -179,38 +227,46 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.0 to n-2: assetoshis output to CC
//vout.n-2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['t'] [assetid]
if ( inputs == 0 )
return eval->Invalid("no asset inputs for transfer");
fprintf(stderr,"transfer validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
//if (inputs == 0)
// return eval->Invalid("no asset inputs for transfer");
//fprintf(stderr,"transfer preliminarily validated %.8f -> %.8f (%d %d)\n",(double)inputs/COIN,(double)outputs/COIN,preventCCvins,preventCCvouts);
return eval->Invalid("invalid asset funcid \'t\'");
break;
case 'b': // buyoffer
//vins.*: normal inputs (bid + change)
//vout.0: amount of bid to unspendable
//vout.1: normal output for change (if any)
//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]
if ( remaining_price == 0 )
// 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,cp->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;
fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cp->unspendableCCaddr);
fprintf(stderr,"buy offer validated to destaddr.(%s)\n",cpAssets->unspendableCCaddr);
break;
case 'o': // cancelbuy
//vin.0: normal input
//vin.1: unspendable.(vout.0 from buyoffer) buyTx.vout[0]
//vin.2: CC marker from buyoffer for txfee
//vout.0: vin.1 value to original pubkey buyTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//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(cp,eval,tmpprice,tmporigpubkey,CCaddr,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 = 2;
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:
@@ -224,36 +280,40 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['B'] [assetid] [remaining asset required] [origpubkey]
preventCCvouts = 4;
if ( (nValue= AssetValidateBuyvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (nValue = AssetValidateBuyvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
else if( numvouts < 4 )
return eval->Invalid("not enough vouts for fillbuy");
else if ( tmporigpubkey != origpubkey )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillbuy");
else
{
if ( nValue != tx.vout[0].nValue+tx.vout[1].nValue )
if( nValue != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillbuy");
else if ( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
else if( tx.vout[4].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change present
{
if ( ConstrainVout(tx.vout[2],1,CCaddr,0) == 0 )
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[3].nValue )
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,CCaddr,inputs) == 0 )
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 ( ValidateBidRemainder(remaining_price,tx.vout[0].nValue,nValue,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
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 )
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 )
if( ConstrainVout(tx.vout[0], 1, cpAssets->unspendableCCaddr, 0) == 0 ) // coins to asset unspendable cc addr
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillbuy");
}
}
fprintf(stderr,"fillbuy validated\n");
//fprintf(stderr,"fillbuy validated\n");
break;
//case 'e': // selloffer
// break; // disable swaps
@@ -261,36 +321,48 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vin.0: normal input
//vin.1+: valid CC output for sale
//vout.0: vin.1 assetoshis output to CC to unspendable
//vout.1: CC output for change (if any)
//vout.2: normal output for change (if any)
//vout.1: CC output for marker
//vout.2: CC output for change (if any)
//vout.3: normal output for change (if any)
//'s'.vout.n-1: opreturn [EVAL_ASSETS] ['s'] [assetid] [amount of native coin required] [origpubkey]
//'e'.vout.n-1: opreturn [EVAL_ASSETS] ['e'] [assetid] [assetid2] [amount of asset2 required] [origpubkey]
preventCCvouts = 1;
if ( remaining_price == 0 )
// 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 )
if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 )
return eval->Invalid("invalid normal vout1 for sellvin");
if( tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) // if cc change presents
{
preventCCvouts++;
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
else if ( tx.vout[0].nValue+tx.vout[1].nValue != inputs )
return eval->Invalid("mismatched vout0+vout1 total for selloffer");
} else if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,inputs) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for selloffer");
if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, 0) == 0 ) // tokens to tokens unspendable cc addr. TODO: this in incorrect, should be assets if we got here!
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
else if( tx.vout[0].nValue + tx.vout[2].nValue != inputs )
return eval->Invalid("mismatched vout0+vout2 total for selloffer");
}
// no cc change:
else if( ConstrainVout(tx.vout[0], 1, (char *)cpTokens->unspendableCCaddr, inputs) == 0 ) // tokens to tokens unspendable cc addr TODO: this in incorrect, should be assets if got here!
return eval->Invalid("mismatched vout0 TokensCCaddr for selloffer");
//fprintf(stderr,"remaining.%d for sell\n",(int32_t)remaining_price);
break;
case 'x': // cancel
case 'x': // cancel sell
//vin.0: normal input
//vin.1: unspendable.(vout.0 from exchange or selloffer) sellTx/exchangeTx.vout[0] inputTx
//vin.2: CC marker from selloffer for txfee
//vout.0: vin.1 assetoshis to original pubkey CC sellTx/exchangeTx.vout[0].nValue -> [origpubkey]
//vout.1: normal output for change (if any)
//vout.1: vin.2 back to users pubkey
//vout.2: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['x'] [assetid]
if ( (assetoshis= AssetValidateSellvin(cp,eval,tmpprice,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (assetoshis = AssetValidateSellvin(cpAssets, eval, tmpprice, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 ) // NOTE:
return(false);
else if ( ConstrainVout(tx.vout[0],1,CCaddr,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 = 2;
preventCCvins = 3;
preventCCvouts = 1;
break;
@@ -303,31 +375,35 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//'S'.vout.2: vin.2 value to original pubkey [origpubkey]
//vout.3: normal output for change (if any)
//'S'.vout.n-1: opreturn [EVAL_ASSETS] ['S'] [assetid] [amount of coin still required] [origpubkey]
if ( (assetoshis= AssetValidateSellvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
if( (assetoshis = AssetValidateSellvin(cpAssets, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
else if( numvouts < 4 )
return eval->Invalid("not enough vouts for fillask");
else if ( tmporigpubkey != origpubkey )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillask");
else
{
if ( assetoshis != tx.vout[0].nValue+tx.vout[1].nValue )
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillask");
if ( ValidateAskRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
if( ValidateAskRemainder(remaining_price, tx.vout[0].nValue, assetoshis, tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillask");
else if ( ConstrainVout(tx.vout[1],1,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 ( remaining_price != 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 )
{
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
return eval->Invalid("mismatched vout0 AssetsCCaddr for fill");
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 ////////////
return eval->Invalid("unexpected assets fillexchange funcid");
break; // disable asset swaps
//vin.0: normal input
@@ -339,51 +415,63 @@ bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
//vout.3: CC output for asset2 change (if any)
//vout.3/4: normal output for change (if any)
//vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey]
if ( AssetExactAmounts(1, cp,inputs,1,outputs,eval,tx,assetid2) == false )
eval->Invalid("asset2 inputs != outputs");
if ( (assetoshis= AssetValidateSellvin(cp,eval,totalunits,tmporigpubkey,CCaddr,origaddr,tx,assetid)) == 0 )
//if ( AssetExactAmounts(false, cp,inputs,outputs,eval,tx,assetid2) == false )
// eval->Invalid("asset2 inputs != outputs");
////////// not implemented yet ////////////
if( (assetoshis= AssetValidateSellvin(cpTokens, eval, totalunits, tmporigpubkey, origTokensCCaddr, origNormalAddr, tx, assetid)) == 0 )
return(false);
else if ( numvouts < 3 )
else if( numvouts < 3 )
return eval->Invalid("not enough vouts for fillex");
else if ( tmporigpubkey != origpubkey )
else if( tmporigpubkey != origpubkey )
return eval->Invalid("mismatched origpubkeys for fillex");
else
{
if ( assetoshis != tx.vout[0].nValue+tx.vout[1].nValue )
if( assetoshis != tx.vout[0].nValue + tx.vout[1].nValue )
return eval->Invalid("locked value doesnt match vout0+1 fillex");
else if ( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
else if( tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 )
////////// not implemented yet ////////////
{
if ( ConstrainVout(tx.vout[2],1,CCaddr,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 )
else if( inputs != tx.vout[2].nValue + tx.vout[3].nValue )
{
fprintf(stderr,"inputs %.8f != %.8f + %.8f\n",(double)inputs/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN);
return eval->Invalid("asset inputs doesnt match vout2+3 fillex");
}
}
else if ( ConstrainVout(tx.vout[2],1,CCaddr,inputs) == 0 )
////////// not implemented yet ////////////
else if( ConstrainVout(tx.vout[2], 1, origTokensCCaddr, inputs) == 0 )
return eval->Invalid("vout2 doesnt match inputs fillex");
else if ( ConstrainVout(tx.vout[1],0,0,0) == 0 )
else if( ConstrainVout(tx.vout[1], 0, 0, 0) == 0 )
return eval->Invalid("vout1 is CC for fillex");
fprintf(stderr,"assets vout0 %llu, vin1 %llu, vout2 %llu -> orig, vout1 %llu, total %llu\n",(long long)tx.vout[0].nValue,(long long)assetoshis,(long long)tx.vout[2].nValue,(long long)tx.vout[1].nValue,(long long)totalunits);
if ( ValidateSwapRemainder(remaining_price,tx.vout[0].nValue,assetoshis,tx.vout[1].nValue,tx.vout[2].nValue,totalunits) == false )
if( ValidateSwapRemainder(remaining_price, tx.vout[0].nValue, assetoshis,tx.vout[1].nValue, tx.vout[2].nValue, totalunits) == false )
return eval->Invalid("mismatched remainder for fillex");
else if ( ConstrainVout(tx.vout[1],1,0,0) == 0 )
else if( ConstrainVout(tx.vout[1], 1, 0, 0) == 0 )
////////// not implemented yet ////////////
return eval->Invalid("normal vout1 for fillex");
else if ( remaining_price != 0 )
else if( remaining_price != 0 )
{
if ( ConstrainVout(tx.vout[0],1,(char *)cp->unspendableCCaddr,0) == 0 )
if( ConstrainVout(tx.vout[0], 1, (char *)cpAssets->unspendableCCaddr, 0) == 0 ) // TODO: unsure about this, but this is not impl yet anyway
return eval->Invalid("mismatched vout0 AssetsCCaddr for fillex");
}
}
fprintf(stderr,"fill validated\n");
////////// not implemented yet ////////////
//fprintf(stderr,"fill validated\n");
break;
default:
fprintf(stderr,"illegal assets funcid.(%c)\n",funcid);
return eval->Invalid("unexpected assets funcid");
break;
//break;
}
return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
// what does this do?
bool bPrevent = PreventCC(eval, tx, preventCCvins, numvins, preventCCvouts, numvouts); // seems we do not need this call as we already checked vouts well
//std::cerr << "AssetsValidate() PreventCC returned=" << bPrevent << std::endl;
return (bPrevent);
}

View File

@@ -73,7 +73,7 @@ bool AuctionExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransactio
bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
return(false); // reject any auction CC for now
return eval->Invalid("no validation yet");
numvins = tx.vin.size();
numvouts = tx.vout.size();
preventCCvins = preventCCvouts = -1;
@@ -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<uint8_t> origpubkey; CTransaction vintx; int32_t n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;

705
src/cc/cclib.cpp Normal file
View File

@@ -0,0 +1,705 @@
/******************************************************************************
* 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 <assert.h>
#include <cryptoconditions.h>
#include "primitives/block.h"
#include "primitives/transaction.h"
#include "script/cc.h"
#include "cc/eval.h"
#include "cc/utils.h"
#include "cc/CCinclude.h"
#include "main.h"
#include "chain.h"
#include "core_io.h"
#include "crosschain.h"
#define FAUCET2SIZE COIN
#define EVAL_FAUCET2 EVAL_FIRSTUSER
#ifdef BUILD_ROGUE
#define EVAL_ROGUE 17
std::string MYCCLIBNAME = (char *)"rogue";
#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<uint8_t> 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 *)"<no args>", 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 *)"<no args>", 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 *)"<no args>", 0, 0, 'D', EVAL_ROGUE },
{ (char *)"rogue", (char *)"games", (char *)"<no args>", 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 *)"<no args>", 0, 0, 'G', EVAL_SUDOKU },
{ (char *)"sudoku", (char *)"txidinfo", (char *)"txid", 1, 1, 'T', EVAL_SUDOKU },
{ (char *)"sudoku", (char *)"pending", (char *)"<no args>", 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];
result.push_back(Pair("result","success"));
result.push_back(Pair("CClib",CClib_name()));
for (i=0; i<sizeof(CClib_methods)/sizeof(*CClib_methods); i++)
{
UniValue obj(UniValue::VOBJ);
obj.push_back(Pair("evalcode",CClib_methods[i].evalcode));
if ( CClib_methods[i].funcid < ' ' || CClib_methods[i].funcid >= 128 )
obj.push_back(Pair("funcid",CClib_methods[i].funcid));
else
{
str[0] = CClib_methods[i].funcid;
str[1] = 0;
obj.push_back(Pair("funcid",str));
}
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));
a.push_back(obj);
}
result.push_back(Pair("methods",a));
return(result);
}
UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr)
{
UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; cJSON *params;
//printf("CClib params.(%s)\n",jsonstr!=0?jsonstr:"");
for (i=0; i<sizeof(CClib_methods)/sizeof(*CClib_methods); i++)
{
if ( cp->evalcode == CClib_methods[i].evalcode && strcmp(method,CClib_methods[i].method) == 0 )
{
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",method));
result.push_back(Pair("error","method not found"));
return(result);
}
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,cmpaddr) == 0 )
return(tx.vout[v].nValue);
}
return(0);
}
bool CClibExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
{
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; i<numvins; i++)
{
//fprintf(stderr,"vini.%d\n",i);
if ( (*cp->ismyvin)(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 faucet2 from mempool");
if ( (assetoshis= IsCClibvout(cp,vinTx,tx.vin[i].prevout.n,cp->unspendableCCaddr)) != 0 )
inputs += assetoshis;
}
}
}
for (i=0; i<numvouts; i++)
{
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
if ( (assetoshis= IsCClibvout(cp,tx,i,cp->unspendableCCaddr)) != 0 )
outputs += assetoshis;
}
if ( inputs != outputs+FAUCET2SIZE+txfee )
{
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
return eval->Invalid("mismatched inputs != outputs + FAUCET2SIZE + txfee");
}
else return(true);
}
bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx,unsigned int nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
std::vector<std::pair<CAddressIndexKey, CAmount> > 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;
if ( numvouts < 1 )
return eval->Invalid("no vouts");
else
{
for (i=0; i<numvins; i++)
{
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
{
fprintf(stderr,"faucetget invalid vini\n");
return eval->Invalid("illegal normal vini");
}
}
//fprintf(stderr,"check amounts\n");
if ( CClibExactAmounts(cp,eval,tx,1,10000) == false )
{
fprintf(stderr,"faucetget invalid amount\n");
return false;
}
else
{
preventCCvouts = 1;
if ( IsCClibvout(cp,tx,0,cp->unspendableCCaddr) != 0 )
{
preventCCvouts++;
i = 1;
} else i = 0;
txid = tx.GetHash();
memcpy(hash,&txid,sizeof(hash));
fprintf(stderr,"check faucetget txid %s %02x/%02x\n",uint256_str(str,txid),hash[0],hash[31]);
if ( tx.vout[i].nValue != FAUCET2SIZE )
return eval->Invalid("invalid faucet output");
else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 )
return eval->Invalid("invalid faucetget txid");
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition());
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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("faucet2 is only for brand new addresses");
}
}
retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
if ( retval != 0 )
fprintf(stderr,"faucet2get validated\n");
else fprintf(stderr,"faucet2get invalid\n");
return(retval);
}
}
}
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,txfee = 10000; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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 == txfee )
continue;
// no need to prevent dup
if ( GetTransaction(txid,vintx,hashBlock,false) != 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()));
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);
}
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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk,cclibpk; CScript opret;
if ( txfee == 0 )
txfee = 10000;
mypk = pubkey2pk(Mypubkey());
cclibpk = GetUnspendable(cp,0);
if ( AddNormalinputs(mtx,mypk,funds+txfee,64) > 0 )
{
mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,cclibpk));
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
}
return("");
}
std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params)
{
CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk,cclibpk; int64_t funds,txfee=0,inputs,CCchange=0,nValue=FAUCET2SIZE; std::string rawhex; uint32_t j; int32_t i,len; uint8_t buf[32768]; bits256 hash;
if ( txfee == 0 )
txfee = 10000;
if ( funcid == 'F' )
{
if ( cJSON_GetArraySize(params) > 0 )
{
funds = (int64_t)jdouble(jitem(params,0),0)*COIN + 0.0000000049;
return(Faucet2Fund(cp,0,funds));
} else return("");
}
else if ( funcid != 'G' )
return("");
cclibpk = GetUnspendable(cp,0);
mypk = pubkey2pk(Mypubkey());
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_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_FAUCET2 << (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 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

File diff suppressed because it is too large Load Diff

117
src/cc/customcc.cpp Normal file
View File

@@ -0,0 +1,117 @@
/*
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<uint8_t> 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
{
// make op_return payload as normal.
CScript opret = custom_opret('1',mypk);
std::vector<std::vector<unsigned char>> vData = std::vector<std::vector<unsigned char>>();
vData.push_back(std::vector<unsigned char>(opret.begin(), opret.end()));
// make vout0 with op_return included as payload.
mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,mypk,&vData));
fprintf(stderr, "vout size2.%li\n", mtx.vout.size());
rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,CScript());
return(custom_rawtxresult(result,rawtx,broadcastflag));
}
return(result);
}
bool has_opret(const CTransaction &tx, uint8_t evalcode)
{
for ( auto vout : tx.vout )
{
if ( vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[1] == evalcode )
return true;
}
return false;
}
bool custom_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx)
{
char expectedaddress[64]; CPubKey pk;
CScript opret; int32_t numvout;
if ( !has_opret(tx, EVAL_CUSTOM) )
{
std::vector<std::vector<unsigned char>> vParams = std::vector<std::vector<unsigned char>>();
CScript dummy;
if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() == 1 )
{
opret << E_MARSHAL(ss << vParams[0]);
}
numvout = 1;
}
else
{
opret = tx.vout[1].scriptPubKey;
numvout = 2;
}
if ( tx.vout.size() != numvout ) // make sure the tx only has appropriate outputs
return eval->Invalid("invalid number of vouts");
else if ( custom_opretdecode(pk,opret) != '1' ) // verify opreturn payload
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");
}

45
src/cc/customcc.h Normal file
View File

@@ -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 *)"<parameter help>", 1, 1, '0', EVAL_CUSTOM }, \
{ (char *)MYCCNAME, (char *)"func1", (char *)"<no args>", 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); \
} \
}

0
src/cc/dapps/cJSON.c Executable file → Normal file
View File

1119
src/cc/dapps/dappstd.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -236,7 +236,7 @@ void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep)
{
fclose(fp);
*lenp = 0;
printf("loadfile null size.(%s)\n",fname);
//printf("loadfile null size.(%s)\n",fname);
return(0);
}
if ( filesize > 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<n; i++)
{
if ((item=jitem(array,i))!=0 && (bits256_nonz(tmptxid=jbits256(item,"txid")))!=0 && (rawtx=get_rawtransaction(refcoin,acname,tmptxid))!=0 && (vins=jarray(&m,rawtx,"vin"))!=0)
{
for (int j=0;j<m;j++)
{
if ((vin=jitem(vins,j))!=0 && validateaddress(refcoin,acname,jstr(vin,"address"),"ismine")!=0)
{
num=1;
break;
}
}
}
if (num==1) break;
}
free_json(array);
}
else return(-1);
if ( num == 0 )
{
if ( (array= get_rawmempool(refcoin,acname)) != 0 )
{
if ( (n= cJSON_GetArraySize(array)) != 0 )
{
for (i=0; i<n; i++)
{
txid = jbits256i(array,i);
if ( tx_has_voutaddress(refcoin,acname,txid,coinaddr) > 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<n; i++)
{
item = jitem(pending,i);
origtxid = jbits256(item,"txid");
completetxid = jbits256(item,"completesigningtxid");
txidaddr = jstr(item,"withdrawtxidaddr");
withdrawtxid= jbits256(item,"withdrawtxid");
double amount = jdouble(item,"amount");
if (validateaddress(refcoin,"",txidaddr,"iswatchonly")==0 && validateaddress(refcoin,"",txidaddr,"ismine")==0)
importaddress(refcoin,"",txidaddr,jstr(item,"txid"),0);
if ( txidaddr != 0 && markerexists(refcoin,"",txidaddr)==0)
importaddress(refcoin,"",txidaddr,jstr(item,"withdrawtxid"),0);
if (is_cJSON_True(jobj(item,"confirmed_or_notarized")) && txidaddr != 0 && markerexists(refcoin,"",txidaddr)==0)
{
cointxid = komodobroadcast(refcoin,"",item);
cointxid = broadcasttx(refcoin,"",item);
if ( bits256_nonz(cointxid) != 0 )
{
withdrawaddr = jstr(item,"withdrawaddr");
fprintf(stderr,"withdraw %.8f %s to %s - %s broadcasted on %s\n",(double)satoshis/SATOSHIDEN,refcoin,withdrawaddr,bits256_str(str,cointxid),refcoin);
gatewaysmarkdone("KMD",acname,origtxid,refcoin);
withdrawaddr = jstr(item,"withdrawaddr");
fprintf(stderr,"### WITHDRAW %.8f %s sent to %s\n",amount,refcoin,withdrawaddr);
txid=gatewaysmarkdone(type,refcoin,acname,completetxid);
if (txid.txid!=zeroid.txid) fprintf(stderr,"### MARKDONE withdraw %s\n",bits256_str(str,withdrawtxid));
else fprintf(stderr,"### MARKDONE error broadcasting tx on %s\n",refcoin);
}
else fprintf(stderr,"### WITHDRAW error broadcasting tx on %s\n",refcoin);
}
}
}
}
free_json(retjson);
}
}
@@ -1094,7 +1039,7 @@ oraclesdata 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff 034
int32_t main(int32_t argc,char **argv)
{
cJSON *clijson,*clijson2,*regjson,*item; int32_t acheight,i,retval,M,N,n,height,prevheight = 0; char *pubkeys,*format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; uint64_t price; bits256 txid;
cJSON *clijson,*clijson2,*regjson,*item; int32_t type,i,retval,M,N,n,height,prevheight = 0; char *pubkeys,*format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; uint64_t price; bits256 txid;
if ( argc < 6 )
{
printf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID [refcoin_cli]\n");
@@ -1114,13 +1059,12 @@ int32_t main(int32_t argc,char **argv)
printf("only formats of L and Ihh are supported now\n");
return(-1);
}
M = N = 1;
acheight = 0;
M = N = 0;
refcoin[0] = 0;
while ( 1 )
{
retstr = 0;
if ( (refcoin[0] == 0 || prevheight < (get_coinheight(refcoin,"") - 10)) && (clijson= get_komodocli("KMD",&retstr,acname,"oraclesinfo",oraclestr,"","","")) != 0 )
if ( (refcoin[0] == 0 || prevheight < (get_coinheight(refcoin,"") - 10)) && (clijson= get_cli(refcoin,&retstr,acname,"oraclesinfo",oraclestr,"","","")) != 0 )
{
if ( refcoin[0] == 0 && jstr(clijson,"name") != 0 )
{
@@ -1131,15 +1075,17 @@ int32_t main(int32_t argc,char **argv)
exit(0);
}
pubkeys=0;
if ( get_gatewaysinfo("KMD",acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) < 0 )
if ( get_gatewaysinfo(0,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) == 0 ) type=0;
else if ( get_gatewaysinfo(1,refcoin,acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr,&pubkeys) == 0 ) type=1;
else
{
printf("cant find bindtxid.(%s)\n",bindtxidstr);
exit(0);
}
if (validateaddress(refcoin,"",depositaddr,"iswatchonly")==0 && validateaddress(refcoin,"",depositaddr,"ismine")==0)
{
if (M==N==1) importaddress(refcoin,"",depositaddr,bindtxidstr,0);
else addmultisigaddress(refcoin,"",M,pubkeys,bindtxidstr);
if (M==1 && N==1) importaddress(refcoin,"",depositaddr,bindtxidstr,0);
else addmultisigaddress(refcoin,"",M,pubkeys);
}
if (pubkeys!=0) free(pubkeys);
printf("set refcoin %s <- %s [%s] M.%d of N.%d\n",depositaddr,refcoin,REFCOIN_CLI,M,N);
@@ -1153,16 +1099,14 @@ int32_t main(int32_t argc,char **argv)
{
if ( (height= get_oracledata(refcoin,"",prevheight,hexstr,sizeof(hexstr),"Ihh")) != 0 )
{
if ( (clijson2= get_komodocli("KMD",&retstr2,acname,"oraclesdata",oraclestr,hexstr,"","")) != 0 )
if ( (clijson2= get_cli(refcoin,&retstr2,acname,"oraclesdata",oraclestr,hexstr,"","")) != 0 )
{
//printf("data.(%s)\n",jprint(clijson2,0));
txid = komodobroadcast("KMD",acname,clijson2);
txid = broadcasttx(refcoin,acname,clijson2);
if ( bits256_nonz(txid) != 0 )
{
prevheight = height;
acheight = get_coinheight(refcoin,"");
printf("%s ht.%d <- %s\n",refcoin,height,hexstr);
update_gatewayspending(refcoin,acname,bindtxidstr,M,N);
update_gatewayspending(type,refcoin,acname,bindtxidstr,M,N);
}
free_json(clijson2);
}

View File

@@ -20,18 +20,18 @@
#include "cJSON.c"
/*
z_migrate: the purpose of z_migrate is to make converting of all sprout outputs into sapling. the usage would be for the user to specify a sapling address and call z_migrate zsaddr, until it returns that there is nothing left to be done.
its main functionality is quite similar to a z_mergetoaddress ANY_ZADDR -> onetime_taddr followed by a z_sendmany onetime_taddr -> zsaddr
since the z_mergetoaddress will take time, it would just queue up an async operation. When it starts, it should see if there are any onetime_taddr with 10000.0001 funds in it, that is a signal for it to do the sapling tx and it can just do that without async as it is fast enough, especially with a taddr input. Maybe it limits itself to one, or it does all possible taddr -> sapling as fast as it can. either is fine as it will be called over and over anyway.
It might be that there is nothing to do, but some operations are pending. in that case it would return such a status. as soon as the operation finishes, there would be more work to do.
the amount sent to the taddr, should be 10000.0001
The GUI or user would be expected to generate a sapling address and then call z_migrate saplingaddr in a loop, until it returns that it is all done. this loop should pause for 10 seconds or so, if z_migrate is just waiting for opid to complete.
*/
z_migrate: the purpose of z_migrate is to make converting of all sprout outputs into sapling. the usage would be for the user to specify a sapling address and call z_migrate zsaddr, until it returns that there is nothing left to be done.
its main functionality is quite similar to a z_mergetoaddress ANY_ZADDR -> onetime_taddr followed by a z_sendmany onetime_taddr -> zsaddr
since the z_mergetoaddress will take time, it would just queue up an async operation. When it starts, it should see if there are any onetime_taddr with 10000.0001 funds in it, that is a signal for it to do the sapling tx and it can just do that without async as it is fast enough, especially with a taddr input. Maybe it limits itself to one, or it does all possible taddr -> sapling as fast as it can. either is fine as it will be called over and over anyway.
It might be that there is nothing to do, but some operations are pending. in that case it would return such a status. as soon as the operation finishes, there would be more work to do.
the amount sent to the taddr, should be 10000.0001
The GUI or user would be expected to generate a sapling address and then call z_migrate saplingaddr in a loop, until it returns that it is all done. this loop should pause for 10 seconds or so, if z_migrate is just waiting for opid to complete.
*/
bits256 zeroid;
@@ -331,7 +331,7 @@ cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char
system(cmdstr);
*retstrp = 0;
if ( (jsonstr= filestr(&fsize,fname)) != 0 )
{
{
jsonstr[strlen(jsonstr)-1]='\0';
//fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr);
if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 )
@@ -599,13 +599,13 @@ int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* comp
cJSON *retjson; char *retstr; int32_t res=0;
if ( (retjson= get_komodocli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","")) != 0 )
{
if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1;
if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1;
free_json(retjson);
}
else if ( retstr != 0 )
{
fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr);
free(retstr);
free(retstr);
}
return (res);
}
@@ -649,21 +649,21 @@ int64_t z_getbalance(char *refcoin,char *acname,char *coinaddr)
int32_t z_exportkey(char *privkey,char *refcoin,char *acname,char *zaddr)
{
cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0; int32_t retval = -1;
cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0;
privkey[0] = 0;
if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_exportkey",zaddr,"","","")) != 0 )
{
fprintf(stderr,"z_exportkey.(%s) %s returned json!\n",refcoin,acname);
free_json(retjson);
return(-1);
}
else if ( retstr != 0 )
{
//printf("retstr %s -> %.8f\n",retstr,dstr(amount));
strcpy(privkey,retstr);
free(retstr);
retval = 0;
return(0);
}
return(retval);
}
int32_t getnewaddress(char *coinaddr,char *refcoin,char *acname)
@@ -795,20 +795,8 @@ 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");
@@ -872,7 +860,7 @@ int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinadd
if ( (vouts= jarray(&numarray,txobj,"vout")) != 0 )
{
for (i=0; i<numarray; i++)
{
{
if ((vout = jitem(vouts,i)) !=0 && (sobj= jobj(vout,"scriptPubKey")) != 0 )
{
if ( (addresses= jarray(&n,sobj,"addresses")) != 0 )
@@ -893,7 +881,7 @@ int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinadd
}
}
// if (hasvout==1 && (vins=jarray(&numarray,txobj,"vin"))!=0)
// {
// {
// for (int i=0;i<numarray;i++)
// {
// if ((vin=jitem(vins,i))!=0 && validateaddress(refcoin,acname,jstr(vin,"address"),"ismine")!=0)
@@ -901,7 +889,7 @@ int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinadd
// retval=1;
// break;
// }
// }
// }
// }
free_json(txobj);
}

View File

@@ -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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,false);
{
LOCK(mempool.cs);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressIndexKey, CAmount> > 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<std::pair<CAddressIndexKey, CAmount> > 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<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;

3682
src/cc/dilithium.c Normal file

File diff suppressed because one or more lines are too long

475
src/cc/dilithium.h Normal file
View File

@@ -0,0 +1,475 @@
#include <stdint.h>
/*
#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 <stdint.h>
//#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 <stdint.h>
//#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 <stdint.h>
//#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 <stdint.h>
#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 <stdint.h>
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

View File

@@ -27,6 +27,8 @@
#include "core_io.h"
#include "crosschain.h"
bool CClib_Dispatch(const CC *cond,Eval *eval,std::vector<uint8_t> paramsNull,const CTransaction &txTo,unsigned int nIn);
char *CClib_name();
Eval* EVAL_TEST = 0;
struct CCcontract_info CCinfos[0x100];
@@ -38,8 +40,9 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn)
pthread_mutex_lock(&KOMODO_CC_mutex);
bool out = eval->Dispatch(cond, tx, nIn);
pthread_mutex_unlock(&KOMODO_CC_mutex);
//fprintf(stderr,"out %d vs %d isValid\n",(int32_t)out,(int32_t)eval->state.IsValid());
assert(eval->state.IsValid() == out);
if ( eval->state.IsValid() != out)
fprintf(stderr,"out %d vs %d isValid\n",(int32_t)out,(int32_t)eval->state.IsValid());
//assert(eval->state.IsValid() == out);
if (eval->state.IsValid()) return true;
@@ -64,13 +67,25 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn)
return Invalid("empty-eval");
uint8_t ecode = cond->code[0];
if ( ASSETCHAINS_CCDISABLES[ecode] != 0 )
{
fprintf(stderr,"%s evalcode.%d %02x\n",txTo.GetHash().GetHex().c_str(),ecode,ecode);
return Invalid("disabled-code, -ac_ccenables didnt include this ecode");
}
std::vector<uint8_t> vparams(cond->code+1, cond->code+cond->codeLength);
if ( ecode >= EVAL_FIRSTUSER && ecode <= EVAL_LASTUSER )
{
if ( ASSETCHAINS_CCLIB.size() > 0 && ASSETCHAINS_CCLIB == CClib_name() )
return CClib_Dispatch(cond,this,vparams,txTo,nIn);
else return Invalid("mismatched -ac_cclib vs CClib_name");
}
cp = &CCinfos[(int32_t)ecode];
if ( cp->didinit == 0 )
{
CCinit(cp,ecode);
cp->didinit = 1;
}
std::vector<uint8_t> vparams(cond->code+1, cond->code+cond->codeLength);
switch ( ecode )
{
case EVAL_IMPORTPAYOUT:
@@ -139,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<unsigned char> scriptVec = std::vector<unsigned char>(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; i<nNotaries; i++) {
if (!seenNotaries[i]) {
if (memcmp(pk, notaries[i], 33) == 0) {
seenNotaries[i] = 1;
goto found;
}
}
}
return false;
found:;
}
return true;
return CheckTxAuthority(tx, auth);
}
/*
* Get MoM from a notarisation tx hash (on KMD)
*/

View File

@@ -55,9 +55,15 @@
EVAL(EVAL_PEGS, 0xee) \
EVAL(EVAL_MARMARA, 0xef) \
EVAL(EVAL_PAYMENTS, 0xf0) \
EVAL(EVAL_GATEWAYS, 0xf1)
EVAL(EVAL_GATEWAYS, 0xf1) \
EVAL(EVAL_TOKENS, 0xf2) \
EVAL(EVAL_IMPORTGATEWAY, 0xf3) \
// evalcodes 0x10 to 0x7f are reserved for cclib dynamic CC
#define EVAL_FIRSTUSER 0x10
#define EVAL_LASTUSER 0x7f
typedef uint8_t EvalCode;

View File

@@ -118,15 +118,15 @@ bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx
else if ( (hash[0] & 0xff) != 0 || (hash[31] & 0xff) != 0 )
return eval->Invalid("invalid faucetget txid");
Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
SetCCtxids(txids,destaddr);
SetCCtxids(txids,destaddr,tx.vout[i].scriptPubKey.IsPayToCryptoCondition());
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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()));

View File

@@ -75,7 +75,7 @@ bool FSMExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &t
bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
return(false); // reject any FSM CC for now
return eval->Invalid("no validation yet");
numvins = tx.vin.size();
numvouts = tx.vout.size();
preventCCvins = preventCCvouts = -1;
@@ -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<uint8_t> origpubkey; CTransaction vintx; int32_t n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;

289
src/cc/games/prices.c Normal file
View File

@@ -0,0 +1,289 @@
#include "prices.h"
#include <time.h>
#include <unistd.h>
#ifdef BUILD_GAMESCC
#include "../rogue/cursesd.h"
#else
#include <curses.h>
#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 <ncurses.h>
#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 *)"<nullstr>";
}
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

281
src/cc/games/prices.cpp Normal file
View File

@@ -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<uint8_t> 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<n; i++)
{
tx = block.vtx[i];
if ( myGetTransaction(tx.vin[vini].prevout.hash,vintx,hashBlock) == 0 )
continue;
else if ( tx.vin[vini].prevout.n >= 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<uint8_t> 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<len&&i<sizeof(price); i++)
((uint8_t *)&price)[7-i] = payload[i];
prices_update((uint32_t)(price >> 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);
}

208
src/cc/games/prices.h Normal file
View File

@@ -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

904
src/cc/games/tetris.c Normal file
View File

@@ -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 <stdio.h> // for FILE
#include <stdbool.h> // for bool
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <time.h>
#include <string.h>
#ifdef BUILD_GAMESCC
#include "../rogue/cursesd.h"
#else
#include <curses.h>
#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 *)"<nullstr>";
}
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

87
src/cc/games/tetris.cpp Normal file
View File

@@ -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<uint8_t> 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<len; i++)
// fprintf(stderr,"%02x",payload[i]);
//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);
}
UniValue games_bet(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
{
UniValue result;
return(result);
}
UniValue games_settle(uint64_t txfee,struct CCcontract_info *cp,cJSON *params)
{
UniValue result;
return(result);
}
bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx)
{
return(true);
}

208
src/cc/games/tetris.h Normal file
View File

@@ -0,0 +1,208 @@
#ifndef H_TETRIS_H
#define H_TETRIS_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 "tetris" // name of executable
#define GAMEMAIN tetris // main program of game
#define GAMEPLAYERJSON tetrisplayerjson // displays game specific json
#define GAMEDATA tetrisdata // extracts data from game specific variables into games_state
#define CHAINNAME "GTEST" // -ac_name=
typedef uint16_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

1811
src/cc/gamescc.cpp Normal file

File diff suppressed because it is too large Load Diff

120
src/cc/gamescc.h Normal file
View File

@@ -0,0 +1,120 @@
#ifndef H_GAMESCC_H
#define H_GAMESCC_H
#include <stdint.h>
#include <stdio.h>
#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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

670
src/cc/heir_validate.h Normal file
View File

@@ -0,0 +1,670 @@
#ifndef HEIR_VALIDATE_H
#define HEIR_VALIDATE_H
#include "CCinclude.h"
#include "CCHeir.h"
#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos)
// makes coin initial tx opret
vscript_t EncodeHeirCreateOpRet(uint8_t funcid, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo);
vscript_t EncodeHeirOpRet(uint8_t funcid, uint256 fundingtxid, uint8_t isHeirSpendingBegan);
uint256 FindLatestFundingTx(uint256 fundingtxid, uint256 &tokenid, CScript& opRetScript, uint8_t &isHeirSpendingBegan);
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, uint256& fundingtxid, uint8_t &isHeirSpendingBegan, bool noLogging = false);
//uint8_t DecodeHeirOpRet(CScript scriptPubKey, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, bool noLogging = false);
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, CPubKey& ownerPubkey, CPubKey& heirPubkey, int64_t& inactivityTime, std::string& heirName, std::string& memo, bool noLogging = false);
uint8_t DecodeHeirEitherOpRet(CScript scriptPubKey, uint256 &tokenid, uint256 &fundingTxidInOpret, uint8_t &hasHeirSpendingBegun, bool noLogging = false);
inline static bool isMyFuncId(uint8_t funcid) { return IS_CHARINSTR(funcid, "FAC"); }
inline static bool isSpendingTx(uint8_t funcid) { return (funcid == 'C'); }
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of coins
class CoinHelper {
public:
static uint8_t getMyEval() { return EVAL_HEIR; }
static int64_t addOwnerInputs(uint256 dummyid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
return AddNormalinputs(mtx, ownerPubkey, total, maxinputs);
}
static CScript makeCreateOpRet(uint256 dummyid, std::vector<CPubKey> 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<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
return CScript() << OP_RETURN << EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan);
}
static CScript makeClaimOpRet(uint256 dummyid, std::vector<CPubKey> dummyPubkeys, uint256 fundingtxid, uint8_t 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);
}
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
}
/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
return CTxOut(amount, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG);
} */
static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) {
struct CCcontract_info *cpHeir, heirC;
cpHeir = CCinit(&heirC, EVAL_HEIR);
return GetCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey);
}
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);
}
};
// helper class to allow polymorphic behaviour for HeirXXX() functions in case of tokens
class TokenHelper {
public:
static uint8_t getMyEval() { return EVAL_TOKENS; }
static int64_t addOwnerInputs(uint256 tokenid, CMutableTransaction& mtx, CPubKey ownerPubkey, int64_t total, int32_t maxinputs) {
struct CCcontract_info *cpHeir, heirC;
cpHeir = CCinit(&heirC, EVAL_TOKENS);
return AddTokenCCInputs(cpHeir, mtx, ownerPubkey, tokenid, total, maxinputs);
}
static CScript makeCreateOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, CPubKey ownerPubkey, CPubKey heirPubkey, int64_t inactivityTimeSec, std::string heirName, std::string memo) {
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
std::make_pair(OPRETID_HEIRDATA, EncodeHeirCreateOpRet((uint8_t)'F', ownerPubkey, heirPubkey, inactivityTimeSec, heirName, memo)));
}
static CScript makeAddOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'A', fundingtxid, isHeirSpendingBegan)));
}
static CScript makeClaimOpRet(uint256 tokenid, std::vector<CPubKey> voutTokenPubkeys, uint256 fundingtxid, uint8_t isHeirSpendingBegan) {
return EncodeTokenOpRet(tokenid, voutTokenPubkeys,
std::make_pair(OPRETID_HEIRDATA, EncodeHeirOpRet((uint8_t)'C', fundingtxid, isHeirSpendingBegan)));
}
static CTxOut make1of2Vout(int64_t amount, CPubKey ownerPubkey, CPubKey heirPubkey) {
return MakeTokensCC1of2vout(EVAL_HEIR, amount, ownerPubkey, heirPubkey);
}
static CTxOut makeUserVout(int64_t amount, CPubKey myPubkey) {
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS
}
/* static CTxOut makeClaimerVout(int64_t amount, CPubKey myPubkey) {
return MakeCC1vout(EVAL_TOKENS, amount, myPubkey); // yes EVAL_TOKENS
} */
static bool GetCoinsOrTokensCCaddress1of2(char *coinaddr, CPubKey ownerPubkey, CPubKey heirPubkey) {
struct CCcontract_info *cpHeir, heirC;
cpHeir = CCinit(&heirC, EVAL_HEIR);
return GetTokensCCaddress1of2(cpHeir, coinaddr, ownerPubkey, heirPubkey);
}
static void CCaddrCoinsOrTokens1of2set(struct CCcontract_info *cp, CPubKey ownerPubkey, CPubKey heirPubkey, char *coinaddr) {
CCaddrTokens1of2set(cp, ownerPubkey, heirPubkey, coinaddr);
}
};
/**
* Small framework for vins and vouts validation implementing a variation of 'chain of responsibility' pattern:
* It consists of two classes CInputValidationPlan and COutputValidationPlan which both are configured with an array of vectors of validators
* (These validators are derived from the class CValidatorBase).
*
* A example of a validator may verify for a vout if its public key corresponds to the public key which is stored in opreturn.
* Or, vin validator may check if this vin depicts correctly to the CC contract's address.
*
* For validating vins CInputValidator additionally is provided with an instance of a class derived from the CInputIdentifierBase class.
* this identifier class allows to select identical vins (for example, normal vins or cc input vins) and apply validators from the corresponding vector to it.
* Note: CInputValidator treats that at least one identified vin should be present, otherwise it returns eval->invalid() and false.
*
* For validating vouts COutputValidator is configured for each vector of validators with the vout index to which these validators are applied
* (see constructors of both CInputValidator and COutputValidator)
*/
/**
* base class for all validators
*/
class CValidatorBase
{
public:
CValidatorBase(CCcontract_info* cp) : m_cp(cp) {}
virtual bool isVinValidator() const = 0;
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const = 0;
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const = 0;
protected:
CCcontract_info * m_cp;
};
/**
* Base class for classes which identify vins as normal or cc inputs
*/
class CInputIdentifierBase
{
public:
CInputIdentifierBase(CCcontract_info* cp) : m_cp(cp) {}
virtual std::string inputName() const = 0;
virtual bool identifyInput(CTxIn vin) const = 0;
protected:
CCcontract_info * m_cp;
};
/**
* Encapsulates an array containing rows of validators
* Each row is a vector of validators (zero is possible) for validating vins or prev tx's vouts
* this validation plan is used for validating tx inputs
*/
template <typename TValidatorBase>
class CInputValidationPlan
{
using ValidatorsRow = std::vector<TValidatorBase*>;
public:
// Pushes a row of validators for validating a vin or vout
// @param CInputIdentifierBase* pointer to class-identifier which determines several identical adjacent vins (like in schema "vin.0+: normal inputs")
// @param pargs parameter pack of zero or more pointer to validator objects
// Why pointers? because we store the base class in validators' row and then call its virtual functions
template <typename TValidatorBaseX, typename... ARGS>
void pushValidators(CInputIdentifierBase *identifier, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
{
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
m_arrayValidators.push_back(std::make_pair(identifier, vValidators));
}
// validate tx inputs and corresponding prev tx vouts
bool validate(const CTransaction& tx, Eval* eval)
{
std::string message = "<empty>";
//std::cerr << "CInputValidationPlan::validate() starting vins validation..." << std::endl;
int32_t ival = 0;
int32_t iv = 0;
int32_t numv = tx.vin.size();
int32_t numValidators = m_arrayValidators.size();
// run over vins:
while (iv < numv && ival < numValidators) {
int32_t identifiedCount = 0;
CInputIdentifierBase *identifier = m_arrayValidators[ival].first;
// check if this is 'our' input:
while (iv < numv && identifier->identifyInput(tx.vin[iv])) {
// get prev tx:
CTransaction prevTx, *pPrevTxOrNull = NULL;
uint256 hashBlock;
if (!eval->GetTxUnconfirmed(tx.vin[iv].prevout.hash, prevTx, hashBlock)) {
std::ostringstream stream;
stream << "can't find vinTx for vin=" << iv << ".";
return eval->Invalid(stream.str().c_str());
}
pPrevTxOrNull = &prevTx; // TODO: get prev tx only if it required (i.e. if vout validators are present)
// exec 'validators' from validator row of ival index, for tx.vin[iv]
if (!execValidatorsInRow(&tx, pPrevTxOrNull, iv, ival, message)) {
std::ostringstream stream;
stream << "invalid tx vin[" << iv << "]:" << message;
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
}
identifiedCount++; // how many vins we identified
iv++; // advance to the next vin
}
// CInputValidationPlan treats that there must be at least one identified vin for configured validators' row
// like in 'vin.0: normal input'
if (identifiedCount == 0) {
std::ostringstream stream;
stream << "can't find required vins for " << identifier->inputName() << ".";
return eval->Invalid(stream.str().c_str());
}
ival++; // advance to the next validator row
// and it will try the same vin with the new CInputIdentifierBase and validators row
}
// validation is successful if all validators have been used (i.e. ival = numValidators)
if (ival < numValidators) {
std::cerr << "CInputValidationPlan::validate() incorrect tx" << " ival=" << ival << " numValidators=" << numValidators << std::endl;
return eval->Invalid("incorrect tx structure: not all required vins are present.");
}
//std::cerr << "CInputValidationPlan::validate() returns with true" << std::endl;
return true;
}
private:
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
bool execValidatorsInRow(const CTransaction* pTx, const CTransaction* pPrevTx, int32_t iv, int32_t ival, std::string& refMessage) const
{
// check boundaries:
if (ival < 0 || ival >= m_arrayValidators.size()) {
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param ival index";
return false;
}
if (iv < 0 || iv >= pTx->vin.size()) {
std::cerr << "CInputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param iv index";
return false;
}
// get requested row of validators:
ValidatorsRow vValidators = m_arrayValidators[ival].second;
//std::cerr << "CInputValidationPlan::execValidatorsInRow() calling validators" << " for vin iv=" << iv << " ival=" << ival << std::endl;
for (auto v : vValidators) {
bool result;
if (v->isVinValidator())
// validate this vin and previous vout:
result = v->validateVin(pTx->vin[iv], pPrevTx->vout, pTx->vin[iv].prevout.n, refMessage);
else
// if it is vout validator pass the previous tx vout:
result = v->validateVout( pPrevTx->vout[pTx->vin[iv].prevout.n], pTx->vin[iv].prevout.n, refMessage);
if (!result) {
return result;
}
}
return true; // validation OK
}
private:
//std::map<CInputIdentifierBase*, ValidatorsRow> m_arrayValidators;
std::vector< std::pair<CInputIdentifierBase*, ValidatorsRow> > m_arrayValidators;
};
/**
* Encapsulates an array containing rows of validators
* Each row is a vector of validators (zero is possible) for validating vouts
* this validation plan is used for validating tx outputs
*/
template <typename TValidatorBase>
class COutputValidationPlan
{
using ValidatorsRow = std::vector<TValidatorBase*>;
public:
// Pushes a row of validators for validating a vout
// @param ivout index to vout to validate
// @param pargs parameter pack of zero or more pointer to validator objects
// Why pointers? because we store base class and call its virtual functions
template <typename TValidatorBaseX, typename... ARGS>
void pushValidators(int32_t ivout, ARGS*... pargs) // validators row passed as variadic arguments CValidatorX *val1, CValidatorY *val2 ...
{
ValidatorsRow vValidators({ (TValidatorBase*)pargs... });
m_arrayValidators.push_back(std::make_pair(ivout, vValidators));
}
// validate tx outputs
bool validate(const CTransaction& tx, Eval* eval)
{
std::string message = "<empty>";
//std::cerr << "COutputValidationPlan::validateOutputs() starting vouts validation..." << std::endl;
int32_t ival = 0;
int32_t numVouts = tx.vout.size();
int32_t numValidators = m_arrayValidators.size();
// run over vouts:
while (ival < numValidators) {
int32_t ivout = m_arrayValidators[ival].first;
if (ivout >= numVouts) {
std::cerr << "COutputValidationPlan::validate() incorrect tx" << "for ival=" << ival << " in tx.vout no such ivout=" << ivout << std::endl;
return eval->Invalid("incorrect tx structure: not all required vouts are present.");
}
else
{
// exec 'validators' from validator row of ival index, for tx.vout[ivout]
if (!execValidatorsInRow(&tx, ivout, ival, message)) {
std::ostringstream stream;
stream << "invalid tx vout[" << ivout << "]:" << message;
return eval->Invalid(stream.str().c_str()); // ... if not, return 'invalid'
}
}
ival++; // advance to the next vout
}
//std::cerr << "COutputValidationPlan::validate() returns with true" << std::endl;
return true;
}
private:
// Executes validators from the requested row of validators (selected by iValidators) for selected vin or vout (selected by iv)
bool execValidatorsInRow(const CTransaction* pTx, int32_t iv, int32_t ival, std::string& refMessage) const
{
// check boundaries:
if (ival < 0 || ival >= m_arrayValidators.size()) {
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param ival=" << ival << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param ival index";
return false;
}
if (iv < 0 || iv >= pTx->vout.size()) {
std::cerr << "COutputValidationPlan::execValidatorsInRow() internal error: incorrect param iv=" << iv << " size=" << m_arrayValidators.size();
refMessage = "internal error: incorrect param iv index";
return false;
}
// get requested row of validators:
ValidatorsRow vValidators = m_arrayValidators[ival].second;
//std::cerr << "COutputValidationPlan::execRow() calling validators" << " for vout iv=" << iv << " ival=" << ival << std::endl;
for (auto v : vValidators) {
if (!v->isVinValidator()) {
// if this is a 'in' validation plan then pass the previous tx vout:
bool result = v->validateVout(pTx->vout[iv], iv, refMessage);
if (!result)
return result;
}
}
return true; // validation OK
}
private:
//std::map<int32_t, ValidatorsRow> m_mapValidators;
std::vector< std::pair<int32_t, ValidatorsRow> > m_arrayValidators;
};
class CNormalInputIdentifier : CInputIdentifierBase {
public:
CNormalInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
virtual std::string inputName() const { return std::string("normal input"); }
virtual bool identifyInput(CTxIn vin) const {
return !IsCCInput(vin.scriptSig);
}
};
class CCCInputIdentifier : CInputIdentifierBase {
public:
CCCInputIdentifier(CCcontract_info* cp) : CInputIdentifierBase(cp) {}
virtual std::string inputName() const { return std::string("CC input"); }
virtual bool identifyInput(CTxIn vin) const {
return IsCCInput(vin.scriptSig);
}
};
/**
* Validates 1of2address for vout (may be used for either this or prev tx)
*/
template <class Helper> class CCC1of2AddressValidator : CValidatorBase
{
public:
CCC1of2AddressValidator(CCcontract_info* cp, CScript opRetScript, std::string customMessage = "") :
m_fundingOpretScript(opRetScript), m_customMessage(customMessage), CValidatorBase(cp) {}
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
{
//std::cerr << "CCC1of2AddressValidator::validateVout() entered" << std::endl;
CPubKey ownerPubkey, heirPubkey;
int64_t inactivityTime;
std::string heirName, memo;
uint256 tokenid;
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;
return false;
}
char shouldBeAddr[65], ccAddr[65];
//GetCCaddress1of2(m_cp, shouldBeAddr, ownerPubkey, heirPubkey);
Helper::GetCoinsOrTokensCCaddress1of2(shouldBeAddr, ownerPubkey, heirPubkey);
if (vout.scriptPubKey.IsPayToCryptoCondition()) {
if (Getscriptaddress(ccAddr, vout.scriptPubKey) && strcmp(shouldBeAddr, ccAddr) == 0) {
//std::cerr << "CCC1of2AddressValidator::validateVout() exits with true" << std::endl;
return true;
}
else {
message = m_customMessage + std::string(" incorrect heir funding address: incorrect pubkey(s)");
}
}
else {
message = m_customMessage + std::string(" incorrect heir funding address: not a 1of2addr");
}
std::cerr << "CCC1of2AddressValidator::validateVout() exits with false: " << message << std::endl;
return false;
}
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return false; }
private:
CScript m_fundingOpretScript;
std::string m_customMessage;
};
/**
* Validates if this is vout to owner or heir from opret (funding or change)
*/
template <class Helper> class CMyPubkeyVoutValidator : CValidatorBase
{
public:
CMyPubkeyVoutValidator(CCcontract_info* cp, CScript opRetScript, bool checkNormals)
: m_fundingOpretScript(opRetScript), m_checkNormals(checkNormals), CValidatorBase(cp) { }
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
{
//std::cerr << "CMyPubkeyVoutValidator::validateVout() entered" << std::endl;
CPubKey ownerPubkey, heirPubkey;
int64_t inactivityTime;
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, memo, true);
if (funcId == 0) {
message = std::string("invalid opreturn format");
return false;
}
CScript ownerScript;
CScript heirScript;
if (m_checkNormals) { //not used, incorrect check, too strict
ownerScript = CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
heirScript = CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(coin,owner)=" << CoinHelper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(coin,heir)=" << CoinHelper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl;
}
else {
ownerScript = Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey;
heirScript = Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey;
std::cerr << "CMyPubkeyVoutValidator::validateVout() vout.scriptPubKey=" << vout.scriptPubKey.ToString() << " makeUserVout(owner)=" << Helper::makeUserVout(vout.nValue, ownerPubkey).scriptPubKey.ToString() << " makeUserVout(heir)=" << Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey.ToString() << std::endl;
}
// recreate scriptPubKey for owner and heir and compare it with that of the vout to check:
if (vout.scriptPubKey == ownerScript || vout.scriptPubKey == heirScript) {
// this is vout to owner or heir addr:
//std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with true" << std::endl;
return true;
}
std::cerr << "CMyPubkeyVoutValidator::validateVout() exits with false (not the owner's or heir's addresses)" << std::endl;
message = std::string("invalid pubkey");
return false;
}
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
//uint256 m_lasttxid;
bool m_checkNormals;
};
/**
* Check if the user is the heir and the heir is allowed to spend (duration > inactivityTime)
*/
template <class Helper> class CHeirSpendValidator : CValidatorBase
{
public:
CHeirSpendValidator(CCcontract_info* cp, CScript opRetScript, uint256 latesttxid, uint8_t isHeirSpendingBegan)
: m_fundingOpretScript(opRetScript), m_latesttxid(latesttxid), m_isHeirSpendingBegan(isHeirSpendingBegan), CValidatorBase(cp) {}
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
{
//std::cerr << "CHeirSpendValidator::validateVout() entered" << std::endl;
CPubKey ownerPubkey, heirPubkey;
int64_t inactivityTime;
std::string heirName, memo;
uint256 tokenid;
// get heir pubkey:
uint8_t funcId = DecodeHeirEitherOpRet(m_fundingOpretScript, tokenid, ownerPubkey, heirPubkey, inactivityTime, heirName, memo, true);
if (funcId == 0) {
message = std::string("invalid opreturn format");
return false;
}
int32_t numblocks;
int64_t durationSec = CCduration(numblocks, m_latesttxid);
// recreate scriptPubKey for heir and compare it with that of the vout:
if (vout.scriptPubKey == Helper::makeUserVout(vout.nValue, heirPubkey).scriptPubKey) {
// this is the heir is trying to spend
if (!m_isHeirSpendingBegan && durationSec <= inactivityTime) {
message = "heir is not allowed yet to spend funds";
std::cerr << "CHeirSpendValidator::validateVout() heir is not allowed yet to spend funds" << std::endl;
return false;
}
else {
// heir is allowed to spend
return true;
}
}
//std::cerr << "CHeirSpendValidator::validateVout() exits with true" << std::endl;
// this is not heir:
return true;
}
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
uint256 m_latesttxid;
uint8_t m_isHeirSpendingBegan;
};
/**
* Validates this opreturn and compares it with the opreturn from the previous tx
*/
template <class Helper> class COpRetValidator : CValidatorBase
{
public:
COpRetValidator(CCcontract_info* cp, CScript opret)
: m_fundingOpretScript(opret), CValidatorBase(cp) {}
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const
{
//std::cerr << "COpRetValidator::validateVout() entered" << std::endl;
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid;
uint8_t dummyIsHeirSpendingBegan;
uint8_t funcId = DecodeHeirEitherOpRet(vout.scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true);
if (funcId == 0) {
message = std::string("invalid opreturn format");
return false;
}
uint8_t initialFuncId = DecodeHeirEitherOpRet(m_fundingOpretScript, initialTokenid, dummyTxid, dummyIsHeirSpendingBegan, true);
if (initialFuncId == 0) {
message = std::string("invalid initial tx opreturn format");
return false;
}
// validation rules:
if (!isMyFuncId(funcId)) {
message = std::string("invalid funcid in opret");
return false;
}
if (typeid(Helper) == typeid(TokenHelper)) {
if (tokenid != initialTokenid) {
message = std::string("invalid tokenid in opret");
return false;
}
}
//std::cerr << "COpRetValidator::validateVout() exits with true" << std::endl;
return true;
}
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
private:
CScript m_fundingOpretScript;
};
/**
* marker spending prevention validator,
* returns false if for tx with funcid=F vout.1 is being tried to spend
*/
template <class Helper> class CMarkerValidator : CValidatorBase
{
public:
CMarkerValidator(CCcontract_info* cp)
: CValidatorBase(cp) { }
virtual bool isVinValidator() const { return true; } // this is vin validator
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; }
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const {
uint256 fundingTxidInOpret = zeroid, dummyTxid, tokenid = zeroid, initialTokenid = zeroid;
uint8_t dummyIsHeirSpendingBegan;
//std::cerr << "CMarkerValidator::validateVin() prevVout.size()=" << prevVout.size() << " prevN=" << prevN << std::endl;
if (prevVout.size() > 0) {
// get funcId for prev tx:
uint8_t funcId = DecodeHeirEitherOpRet(prevVout[prevVout.size()-1].scriptPubKey, tokenid, fundingTxidInOpret, dummyIsHeirSpendingBegan, true);
//std::cerr << "CMarkerValidator::validateVin() funcId=" << (funcId?funcId:' ') << std::endl;
if (funcId == 'F' && prevN == 1) { // do not allow to spend 'F' marker's vout
message = std::string("spending marker not allowed");
return false;
}
}
//std::cerr << "CMarkerValidator::validateVin() exits with true" << std::endl;
return true;
}
};
/**
* empty validator always returns true
*/
template <class Helper> class CNullValidator : CValidatorBase
{
public:
CNullValidator(CCcontract_info* cp)
: CValidatorBase(cp) { }
virtual bool isVinValidator() const { return false; }
virtual bool validateVout(CTxOut vout, int32_t vout_n, std::string& message) const { return true; }
virtual bool validateVin(CTxIn vin, std::vector<CTxOut> prevVout, int32_t prevN, std::string& message) const { return true; }
};
#endif

View File

@@ -19,77 +19,366 @@
#include "crosschain.h"
#include "primitives/transaction.h"
#include "cc/CCinclude.h"
#include <openssl/sha.h>
/*
* 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<uint8_t> rawtx,uint256 txid,std::vector<uint8_t> rawproof) // find burnTx with hash from "other" daemon
// utilities from gateways.cpp
uint256 BitcoinGetProofMerkleRoot(const std::vector<uint8_t> &proofData, std::vector<uint256> &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<CPubKey> &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<CPubKey> publishers, std::vector<uint256>txids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vector<uint8_t>proof, 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<CPubKey> publishers, std::vector<uint256>txids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vector<uint8_t>proof, 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::vector<uint8_t>proof, uint256 merkleroot)
{
std::vector<uint256> 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<uint8_t> 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<CTxOut> vouts;
// std::vector<CPubKey> pubkeys; std::vector<uint256>txids;
// char markeraddr[64]; int64_t datafee;
// std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<uint256> 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<CTxOut> vouts)
{
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()),burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
CPubKey mypk; uint256 codaburntxid; std::vector<unsigned char> 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<uint8_t> rawproof,CTransaction
return(-1);
}
int32_t CheckCODAimport(TxProof proof,std::vector<uint8_t> rawproof,CTransaction burnTx,std::vector<CTxOut> payouts)
int32_t CheckCODAimport(CTransaction importTx,CTransaction burnTx,std::vector<CTxOut> 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<uint8_t> rawproof,CTransaction burnTx,std::vector<CTxOut> payouts)
int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction burnTx,std::string refcoin,std::vector<uint8_t> proof,
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256> 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<CTxOut> vouts;
// std::vector<CPubKey> pubkeys; std::vector<uint256>txids;
// char markeraddr[64]; int64_t datafee;
// std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<uint8_t> rawproof,CTransaction burnTx,std::vector<CTxOut> 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<uint8_t> 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<uint8_t> params,const CTransaction &importTx,unsigned int nIn)
{
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; uint64_t txfee = 10000;
uint32_t targetCcid; std::string targetSymbol; uint256 payoutsHash; std::vector<uint8_t> rawproof;
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; uint64_t txfee = 10000; int32_t height,burnvout; std::vector<CPubKey> publishers;
uint32_t targetCcid; std::string targetSymbol,srcaddr,destaddr,receipt,rawburntx; uint256 payoutsHash,bindtxid; std::vector<uint8_t> rawproof;
std::vector<uint256> txids; CPubKey destpub;
if ( importTx.vout.size() < 2 )
return Invalid("too-few-vouts");
// params
@@ -132,10 +592,10 @@ bool Eval::ImportCoin(const std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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();
}

1292
src/cc/importgateway.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -35,8 +35,8 @@
* *
******************************************************************************/
#ifndef cJSON__h
#define cJSON__h
#ifndef cJSON__ccih
#define cJSON__ccih
#include <stdio.h>
#include <stdlib.h>

View File

@@ -115,7 +115,7 @@ bool LottoExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction
bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn)
{
int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval;
return(false); // reject any lotto CC for now
return eval->Invalid("no validation yet");
numvins = tx.vin.size();
numvouts = tx.vout.size();
preventCCvins = preventCCvouts = -1;
@@ -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<uint8_t> origpubkey; CTransaction vintx; int32_t n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
lockedfunds = 0;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressIndexKey, CAmount> > 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<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
{
txid = it->first.txhash;

19
src/cc/makecclib Executable file
View File

@@ -0,0 +1,19 @@
#!/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

7
src/cc/makecustom Executable file
View File

@@ -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

7
src/cc/makegames Executable file
View File

@@ -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

8
src/cc/makeprices Executable file
View File

@@ -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 ..

37
src/cc/makerogue Executable file
View File

@@ -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

7
src/cc/maketetris Executable file
View File

@@ -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 ..

File diff suppressed because it is too large Load Diff

868
src/cc/musig.cpp Normal file
View File

@@ -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 <struct musig_info *> 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; i<num; i++)
mp->commitment_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<uint8_t> 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<uint8_t> 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<uint8_t> &musig64,CScript scriptPubKey)
{
std::vector<uint8_t> 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<secp256k1_pubkey> 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<n; i++)
{
if ( musig_parsepubkey(ctx,spk,jitem(params,i)) < 0 )
return(cclib_error(result,"error parsing pk"));
pubkeys.push_back(spk);
}
if ( secp256k1_musig_pubkey_combine(ctx,NULL,&combined_pk,pkhash,&pubkeys[0],n) > 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; i<MUSIG[myind]->num; 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<uint8_t> 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<uint8_t> 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");
}

View File

@@ -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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
SetCCunspents(unspentOutputs,markeraddr);
SetCCunspents(unspentOutputs,markeraddr,false);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<uint8_t> vopret,data;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
SetCCunspents(unspentOutputs,batonaddr);
SetCCunspents(unspentOutputs,batonaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
@@ -525,14 +525,14 @@ int32_t oracleprice_add(std::vector<struct oracleprice_info> &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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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 <uint8_t> data; struct CCcontract_info *cp,C; std::vector <struct oracleprice_info> publishers; std::vector <int64_t> prices;
if ( format[0] != 'L' )
return(0);
cp = CCinit(&C,EVAL_ORACLES);
SetCCunspents(unspentOutputs,markeraddr);
SetCCunspents(unspentOutputs,markeraddr,false);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<uint8_t> origpubkey,data; CTransaction vintx; int32_t numvouts,vout,n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressIndexKey, CAmount> > addressIndex;
GetCCaddress(cp,coinaddr,publisher);
SetCCtxids(addressIndex,coinaddr);
SetCCtxids(addressIndex,coinaddr,true);
//fprintf(stderr,"scan lifetime of %s\n",coinaddr);
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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 <uint8_t> 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 <uint8_t> 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<std::pair<CAddressIndexKey, CAmount> > 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<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
{
txid = it->first.txhash;

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More