diff --git a/.gitignore b/.gitignore index d139071e0..a4396df73 100644 --- a/.gitignore +++ b/.gitignore @@ -132,6 +132,7 @@ src/cc/rogue/rogue src/cc/rogue/rogue.so src/cc/rogue/test.zip +src/cc/dapps/a.out src/checkfile src/foo.zip @@ -145,8 +146,6 @@ src/rogue.530623577502174316.pack src/rogue.530623577502174316.player -src/cc/rogue/config.h - src/cc/rogue/config.h src/ROGUE.conf diff --git a/README.md b/README.md index 6c0002ba4..d12521765 100644 --- a/README.md +++ b/README.md @@ -38,7 +38,7 @@ This is the official Komodo sourcecode repository based on https://github.com/jl ## Tech Specification - Max Supply: 200 million KMD -- Block Time: 1m 2s +- Block Time: 60 seconds - Block Reward: 3 KMD - Mining Algorithm: Equihash @@ -84,7 +84,7 @@ brew update brew upgrade brew tap discoteq/discoteq; brew install flock brew install autoconf autogen automake -brew install gcc@6 +brew update && brew install gcc@8 brew install binutils brew install protobuf brew install coreutils diff --git a/depends/builders/darwin.mk b/depends/builders/darwin.mk index f9b066fcf..cbbc5a667 100644 --- a/depends/builders/darwin.mk +++ b/depends/builders/darwin.mk @@ -1,5 +1,5 @@ -build_darwin_CC = gcc-6 -build_darwin_CXX = g++-6 +build_darwin_CC = gcc-8 +build_darwin_CXX = g++-8 build_darwin_AR: = $(shell xcrun -f ar) build_darwin_RANLIB: = $(shell xcrun -f ranlib) build_darwin_STRIP: = $(shell xcrun -f strip) @@ -10,8 +10,8 @@ build_darwin_SHA256SUM = shasum -a 256 build_darwin_DOWNLOAD = curl --connect-timeout $(DOWNLOAD_CONNECT_TIMEOUT) --retry $(DOWNLOAD_RETRIES) -L -f -o #darwin host on darwin builder. overrides darwin host preferences. -darwin_CC= gcc-6 -darwin_CXX= g++-6 +darwin_CC= gcc-8 +darwin_CXX= g++-8 darwin_AR:=$(shell xcrun -f ar) darwin_RANLIB:=$(shell xcrun -f ranlib) darwin_STRIP:=$(shell xcrun -f strip) diff --git a/depends/hosts/darwin.mk b/depends/hosts/darwin.mk index d01e8b5a3..7be744aeb 100644 --- a/depends/hosts/darwin.mk +++ b/depends/hosts/darwin.mk @@ -1,9 +1,9 @@ -OSX_MIN_VERSION=10.8 -OSX_SDK_VERSION=10.11 +OSX_MIN_VERSION=10.12 +OSX_SDK_VERSION=10.12 OSX_SDK=$(SDK_PATH)/MacOSX$(OSX_SDK_VERSION).sdk LD64_VERSION=253.9 -darwin_CC=gcc-6 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) -darwin_CXX=g++-6 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +darwin_CC=gcc-8 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) +darwin_CXX=g++-8 -target $(host) -mmacosx-version-min=$(OSX_MIN_VERSION) --sysroot $(OSX_SDK) -mlinker-version=$(LD64_VERSION) darwin_CFLAGS=-pipe darwin_CXXFLAGS=$(darwin_CFLAGS) diff --git a/qa/rpc-tests/cryptoconditions_oracles.py b/qa/rpc-tests/cryptoconditions_oracles.py index 953df5ca9..1db33bef7 100755 --- a/qa/rpc-tests/cryptoconditions_oracles.py +++ b/qa/rpc-tests/cryptoconditions_oracles.py @@ -3,7 +3,6 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. - from test_framework.test_framework import CryptoconditionsTestFramework from test_framework.authproxy import JSONRPCException from test_framework.util import assert_equal, assert_greater_than, \ @@ -11,13 +10,11 @@ from test_framework.util import assert_equal, assert_greater_than, \ stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises from cryptoconditions import assert_success, assert_error, generate_random_string - class CryptoconditionsOraclesTest(CryptoconditionsTestFramework): def run_oracles_tests(self): rpc = self.nodes[0] rpc1 = self.nodes[1] - result = rpc1.oraclesaddress() result = rpc.oraclesaddress() @@ -54,159 +51,197 @@ class CryptoconditionsOraclesTest(CryptoconditionsTestFramework): too_long_description = generate_random_string(4100) result = rpc.oraclescreate("Test", too_long_description, "s") assert_error(result) + + # need uxtos to create oracle? Crashes if without generate + rpc.generate(2) + # valid creating oracles of different types # using such naming to re-use it for data publishing / reading (e.g. oracle_s for s type) + valid_formats = ["s", "S", "d", "D", "c", "C", "t", "T", "i", "I", "l", "L", "h", "Ihh"] for f in valid_formats: - result = rpc.oraclescreate("Test", "Test", f) + result = rpc.oraclescreate("Test_"+f, "Test_"+f, f) assert_success(result) globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex'], rpc) # trying to register with negative datafee - for f in valid_formats: result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "-100") assert_error(result) # trying to register with zero datafee - for f in valid_formats: result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "0") assert_error(result) # trying to register with datafee less than txfee - for f in valid_formats: result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "500") assert_error(result) - # trying to register valid - for f in valid_formats: + # trying to register valid (unfunded) result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "10000") + assert_error(result) + + # Fund the oracles + result = rpc.oraclesfund(globals()["oracle_{}".format(f)]) + assert_success(result) + fund_txid = self.send_and_mine(result["hex"], rpc) + assert fund_txid, "got txid" + + # trying to register valid (funded) + result = rpc.oraclesregister(globals()["oracle_{}".format(f)], "10000") + print(f) assert_success(result) register_txid = self.send_and_mine(result["hex"], rpc) assert register_txid, "got txid" # TODO: for most of the non valid oraclesregister and oraclessubscribe transactions generating and broadcasting now # so trying only valid oraclessubscribe atm - for f in valid_formats: result = rpc.oraclessubscribe(globals()["oracle_{}".format(f)], self.pubkey, "1") assert_success(result) subscribe_txid = self.send_and_mine(result["hex"], rpc) assert register_txid, "got txid" + rpc.generate(1) + # now lets publish and read valid data for each oracle type # s type result = rpc.oraclesdata(globals()["oracle_{}".format("s")], "05416e746f6e") assert_success(result) - # baton - oraclesdata_s = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("s")], oraclesdata_s, "1") - assert_equal("[u'Anton']", str(result["samples"][0]), "Data match") + oraclesdata_s = self.send_and_mine(result["hex"], rpc) + info = rpc.oraclesinfo(globals()["oracle_{}".format("s")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("s")], batonaddr, "1") + assert_equal("[u'Anton']", str(result["samples"][0]['data']), "Data match") # S type result = rpc.oraclesdata(globals()["oracle_{}".format("S")], "000161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161") assert_success(result) - # baton oraclesdata_S = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("S")], oraclesdata_S, "1") - assert_equal("[u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("S")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("S")], batonaddr, "1") + assert_equal("[u'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa']", str(result["samples"][0]['data']), "Data match") # d type result = rpc.oraclesdata(globals()["oracle_{}".format("d")], "0101") assert_success(result) # baton oraclesdata_d = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("d")], oraclesdata_d, "1") - assert_equal("[u'01']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("d")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("d")], batonaddr, "1") + assert_equal("[u'01']", str(result["samples"][0]['data']), "Data match") # D type 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") - assert_equal("[u'01']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("D")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("D")], batonaddr, "1") + assert_equal("[u'01']", str(result["samples"][0]['data']), "Data match") # c type result = rpc.oraclesdata(globals()["oracle_{}".format("c")], "ff") assert_success(result) # baton oraclesdata_c = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("c")], oraclesdata_c, "1") - assert_equal("[u'-1']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("c")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("c")], batonaddr, "1") + assert_equal("[u'-1']", str(result["samples"][0]['data']), "Data match") # C type result = rpc.oraclesdata(globals()["oracle_{}".format("C")], "ff") assert_success(result) # baton oraclesdata_C = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("C")], oraclesdata_C, "1") - assert_equal("[u'255']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("C")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("C")], batonaddr, "1") + assert_equal("[u'255']", str(result["samples"][0]['data']), "Data match") # t type result = rpc.oraclesdata(globals()["oracle_{}".format("t")], "ffff") assert_success(result) # baton oraclesdata_t = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("t")], oraclesdata_t, "1") - assert_equal("[u'-1']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("t")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("t")], batonaddr, "1") + assert_equal("[u'-1']", str(result["samples"][0]['data']), "Data match") # T type result = rpc.oraclesdata(globals()["oracle_{}".format("T")], "ffff") assert_success(result) # baton oraclesdata_T = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("T")], oraclesdata_T, "1") - assert_equal("[u'65535']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("T")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("T")], batonaddr, "1") + assert_equal("[u'65535']", str(result["samples"][0]['data']), "Data match") # i type result = rpc.oraclesdata(globals()["oracle_{}".format("i")], "ffffffff") assert_success(result) # baton oraclesdata_i = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("i")], oraclesdata_i, "1") - assert_equal("[u'-1']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("i")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("i")], batonaddr, "1") + assert_equal("[u'-1']", str(result["samples"][0]['data']), "Data match") # I type result = rpc.oraclesdata(globals()["oracle_{}".format("I")], "ffffffff") assert_success(result) # baton oraclesdata_I = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("I")], oraclesdata_I, "1") - assert_equal("[u'4294967295']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("I")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("I")], batonaddr, "1") + assert_equal("[u'4294967295']", str(result["samples"][0]['data']), "Data match") # l type result = rpc.oraclesdata(globals()["oracle_{}".format("l")], "00000000ffffffff") assert_success(result) # baton oraclesdata_l = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("l")], oraclesdata_l, "1") - # TODO: working not correct now! - #assert_equal("[u'-4294967296']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("l")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("l")], batonaddr, "1") + assert_equal("[u'-4294967296']", str(result["samples"][0]['data']), "Data match") # L type result = rpc.oraclesdata(globals()["oracle_{}".format("L")], "00000000ffffffff") assert_success(result) # baton oraclesdata_L = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("L")], oraclesdata_L, "1") - assert_equal("[u'18446744069414584320']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("L")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("L")], batonaddr, "1") + assert_equal("[u'18446744069414584320']", str(result["samples"][0]['data']), "Data match") # h type result = rpc.oraclesdata(globals()["oracle_{}".format("h")], "00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffffff") assert_success(result) # baton oraclesdata_h = self.send_and_mine(result["hex"], rpc) - result = rpc.oraclessamples(globals()["oracle_{}".format("h")], oraclesdata_h, "1") - assert_equal("[u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]), "Data match") + info = rpc.oraclesinfo(globals()["oracle_{}".format("h")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("h")], batonaddr, "1") + assert_equal("[u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]['data']), "Data match") # Ihh type 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'4294967295', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]), "Data match") - + info = rpc.oraclesinfo(globals()["oracle_{}".format("Ihh")]) + batonaddr = info['registered'][0]['baton'] + result = rpc.oraclessamples(globals()["oracle_{}".format("Ihh")], batonaddr, "1") + print(result) + assert_equal("[u'4294967295', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000', u'ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000']", str(result["samples"][0]['data']), "Data match") def run_test(self): print("Mining blocks...") diff --git a/src/Makefile.am b/src/Makefile.am index 7e1647a6c..a7dd21be8 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -524,6 +524,11 @@ if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif +if ENABLE_TESTS +libbitcoin_server_a_SOURCES += rpc/testtransactions.cpp +endif + + # cli: zcash-cli libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) @@ -594,11 +599,19 @@ if ENABLE_PROTON komodod_LDADD += $(LIBBITCOIN_PROTON) $(PROTON_LIBS) endif +# [+] Decker: use static linking for libstdc++.6.dylib, libgomp.1.dylib, libgcc_s.1.dylib +if TARGET_DARWIN +komodod_LDFLAGS += -static-libgcc +endif + # bitcoin-cli binary # komodo_cli_SOURCES = bitcoin-cli.cpp komodo_cli_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) $(EVENT_CFLAGS) komodo_cli_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) komodo_cli_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS) +if TARGET_DARWIN +komodo_cli_LDFLAGS += -static-libgcc +endif # wallet-utility binary # if ENABLE_WALLET diff --git a/src/ac/k64 b/src/ac/k64 new file mode 100755 index 000000000..a3b3bc835 --- /dev/null +++ b/src/ac/k64 @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=K64 $1 $2 $3 $4 $5 $6 diff --git a/src/ac/zex b/src/ac/zex deleted file mode 100755 index f4573c093..000000000 --- a/src/ac/zex +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=ZEX $1 $2 $3 $4 $5 $6 diff --git a/src/ac/zexo b/src/ac/zexo new file mode 100755 index 000000000..b6fd508f2 --- /dev/null +++ b/src/ac/zexo @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=ZEXO $1 $2 $3 $4 $5 $6 diff --git a/src/assetchains.json b/src/assetchains.json index 99c6557b6..86dc77565 100644 --- a/src/assetchains.json +++ b/src/assetchains.json @@ -159,18 +159,6 @@ "136.243.102.225" ] }, - { - "ac_name": "MGNX", - "ac_supply": "12465003", - "ac_staked": "90", - "ac_reward": "2000000000", - "ac_halving": "525960", - "ac_cc": "2", - "ac_end": "2629800", - "addnode": [ - "142.93.27.180" - ] - }, { "ac_name": "PGT", "ac_supply": "10000000", @@ -202,17 +190,6 @@ "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", @@ -268,5 +245,28 @@ "ac_name": "KOIN", "ac_supply": "125000000", "addnode": ["3.0.32.10"] + }, + { + "ac_name": "ZEXO", + "ac_reward": "1478310502", + "ac_halving": "525600", + "ac_cc": "42", + "ac_ccenable": "236", + "ac_supply": "100000000", + "ac_perc": "77700", + "ac_staked": "93", + "ac_pubkey": "02713bd85e054db923694b6b7a85306264edf4d6bd6d331814f2b40af444b3ebbc", + "ac_public": "1", + "addnode": [ + "195.201.20.230", + "80.240.17.222" + ] + }, + { + "ac_name": "K64", + "ac_reward": "0", + "ac_supply": "64000777", + "ac_staked": "10", + "addnode": ["18.197.20.21"] } ] diff --git a/src/assetchains.old b/src/assetchains.old index a0cbd3b9c..4cf427c4d 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -39,11 +39,10 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=SEC -ac_cc=333 -ac_supply=1000000000 -addnode=185.148.145.43 & ./komodod -pubkey=$pubkey -ac_name=CCL -ac_supply=200000000 -ac_end=1 -ac_cc=2 -addressindex=1 -spentindex=1 -addnode=142.93.136.89 -addnode=195.201.22.89 & ./komodod -pubkey=$pubkey -ac_name=PIRATE -ac_supply=0 -ac_reward=25600000000 -ac_halving=77777 -ac_private=1 -addnode=178.63.77.56 & -./komodod -pubkey=$pubkey -ac_name=MGNX -ac_supply=12465003 -ac_staked=90 -ac_reward=2000000000 -ac_halving=525960 -ac_cc=2 -ac_end=2629800 -addnode=142.93.27.180 & +#./komodod -pubkey=$pubkey -ac_name=MGNX -ac_supply=12465003 -ac_staked=90 -ac_reward=2000000000 -ac_halving=525960 -ac_cc=2 -ac_end=2629800 -addnode=142.93.27.180 & ./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 & @@ -51,3 +50,5 @@ echo $pubkey ./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 & ./komodod -pubkey=$pubkey -ac_name=KOIN -ac_supply=125000000 -addnode=3.0.32.10 & +./komodod -pubkey=$pubkey -ac_name=ZEXO -ac_supply=100000000 -ac_reward=1478310502 -ac_halving=525600 -ac_cc=42 -ac_ccenable=236 -ac_perc=77700 -ac_staked=93 -ac_pubkey=02713bd85e054db923694b6b7a85306264edf4d6bd6d331814f2b40af444b3ebbc -ac_public=1 -addnode=80.240.17.222 & +./komodod -pubkey=$pubkey -ac_name=K64 -ac_supply=64000777 -ac_reward=0 -ac_staked=10 -addnode=18.197.20.211 & diff --git a/src/cc/CCOracles.h b/src/cc/CCOracles.h index 7f952e5f5..9f6a35966 100644 --- a/src/cc/CCOracles.h +++ b/src/cc/CCOracles.h @@ -21,12 +21,12 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string OracleCreate(int64_t txfee,std::string name,std::string description,std::string format); +std::string OracleFund(int64_t txfee,uint256 oracletxid); std::string OracleRegister(int64_t txfee,uint256 oracletxid,int64_t datafee); std::string OracleSubscribe(int64_t txfee,uint256 oracletxid,CPubKey publisher,int64_t amount); std::string OracleData(int64_t txfee,uint256 oracletxid,std::vector data); - // CCcustom -UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num); +UniValue OracleDataSamples(uint256 reforacletxid,char* batonaddr,int32_t num); UniValue OracleInfo(uint256 origtxid); UniValue OraclesList(); diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 247a0f2ec..ac5f22c47 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -18,17 +18,26 @@ #define CC_PAYMENTS_H #include "CCinclude.h" +#include +#include #define PAYMENTS_TXFEE 10000 +#define PAYMENTS_MERGEOFSET 60 // 1H extra. +extern std::vector > vAddressSnapshot; +extern int32_t lastSnapShotHeight; bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr); +UniValue PaymentsAirdropTokens(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr); UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr); #endif + diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 3dbe6a87c..3779111a6 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -17,34 +17,46 @@ #ifndef CC_PRICES_H #define CC_PRICES_H +#include "komodo_defs.h" #include "CCinclude.h" int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); +extern void GetKomodoEarlytxidScriptPub(); +extern CScript KOMODO_EARLYTXID_SCRIPTPUB; -#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +// #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) // defined in komodo_defs.h #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) +#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) // actually 1111 1000 0000 0000 +#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) // 0000 1000 0000 0000 +#define PRICES_MULT (KOMODO_MAXPRICES * 2) // 0001 0000 0000 0000 +#define PRICES_DIV (KOMODO_MAXPRICES * 3) // 0001 1000 0000 0000 +#define PRICES_INV (KOMODO_MAXPRICES * 4) // 0010 0000 0000 0000 +#define PRICES_MDD (KOMODO_MAXPRICES * 5) // 0010 1000 0000 0000 +#define PRICES_MMD (KOMODO_MAXPRICES * 6) // 0011 0000 0000 0000 +#define PRICES_MMM (KOMODO_MAXPRICES * 7) // 0011 1000 0000 0000 +#define PRICES_DDD (KOMODO_MAXPRICES * 8) // 0100 0000 0000 0000 + +//#define PRICES_NORMFACTOR (int64_t)(SATOSHIDEN) +//#define PRICES_POINTFACTOR (int64_t)10000 + +#define PRICES_REVSHAREDUST 10000 +#define PRICES_SUBREVSHAREFEE(amount) ((amount) * 199 / 200) // revshare fee percentage == 0.005 +#define PRICES_MINAVAILFUNDFRACTION 0.1 // leveraged bet limit < fund fraction bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); -UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount); -UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid); -UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight); -UniValue PricesCashout(uint64_t txfee,uint256 bettxid); +UniValue PricesBet(int64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); +UniValue PricesAddFunding(int64_t txfee,uint256 bettxid,int64_t amount); +UniValue PricesSetcostbasis(int64_t txfee,uint256 bettxid); +UniValue PricesRekt(int64_t txfee,uint256 bettxid,int32_t rektheight); +UniValue PricesCashout(int64_t txfee,uint256 bettxid); UniValue PricesInfo(uint256 bettxid,int32_t refheight); -UniValue PricesList(); +UniValue PricesList(uint32_t filter, CPubKey mypk); +UniValue PricesGetOrderbook(); +UniValue PricesRefillFund(int64_t amount); #endif diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index db1ee7475..3681cf2f3 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -229,7 +229,7 @@ uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t entropyvout,i CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk, std::vector>* vData = NULL); CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2, std::vector>* vData = NULL); int32_t has_opret(const CTransaction &tx, uint8_t evalcode); -CScript getCCopret(const CScript &scriptPubKey); +bool getCCopret(const CScript &scriptPubKey, CScript &opret); bool makeCCopret(CScript &opret, std::vector> &vData); CC *MakeCCcond1(uint8_t evalcode,CPubKey pk); CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2); @@ -251,7 +251,7 @@ void CCaddrTokens1of2set(struct CCcontract_info *cp, CPubKey pk1, CPubKey pk2, c 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); +bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime,int64_t &actualtxfee); int32_t unstringbits(char *buf,uint64_t bits); uint64_t stringbits(char *str); uint256 revuint256(uint256 txid); @@ -298,6 +298,7 @@ UniValue ValueFromAmount(const CAmount& amount); int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey); int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey); +inline std::string STR_TOLOWER(const std::string &str) { std::string out; for (std::string::const_iterator i = str.begin(); i != str.end(); i++) out += std::tolower(*i); return out; } // bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream: #define CCLOG_INFO 0 diff --git a/src/cc/CCtokens.cpp b/src/cc/CCtokens.cpp index be3cbd922..5f5e56ebb 100644 --- a/src/cc/CCtokens.cpp +++ b/src/cc/CCtokens.cpp @@ -937,6 +937,12 @@ UniValue TokenInfo(uint256 tokenid) result.push_back(Pair("error", "cant find tokenid")); return(result); } + if (hashBlock.IsNull()) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "the transaction is still in mempool")); + return(result); + } + if (tokenbaseTx.vout.size() > 0 && DecodeTokenCreateOpRet(tokenbaseTx.vout[tokenbaseTx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c') { LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl); diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index f47bc4212..5de5ed0db 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -63,25 +63,28 @@ int32_t has_opret(const CTransaction &tx, uint8_t evalcode) int i = 0; for ( auto vout : tx.vout ) { - if ( vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[1] == evalcode ) + //fprintf(stderr, "[txid.%s] 1.%i 2.%i 3.%i 4.%i\n",tx.GetHash().GetHex().c_str(), vout.scriptPubKey[0], vout.scriptPubKey[1], vout.scriptPubKey[2], vout.scriptPubKey[3]); + if ( vout.scriptPubKey.size() > 3 && vout.scriptPubKey[0] == OP_RETURN && vout.scriptPubKey[2] == evalcode ) return i; i++; } return 0; } -CScript getCCopret(const CScript &scriptPubKey) +bool getCCopret(const CScript &scriptPubKey, CScript &opret) { std::vector> vParams = std::vector>(); - CScript dummy; CScript opret; - if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) ) + CScript dummy; bool ret = false; + if ( scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) != 0 ) { - //opret << E_MARSHAL(ss << vParams[0]); - opret = CScript(vParams[0].begin()+6, vParams[0].end()); + ret = true; + if ( vParams.size() == 1) + { + opret = CScript(vParams[0].begin()+6, vParams[0].end()); + //fprintf(stderr, "vparams.%s\n", HexStr(vParams[0].begin(), vParams[0].end()).c_str()); + } } - //fprintf(stderr, "params_size.%li parmas_hexstr.%s\n", vParams.size(), HexStr(vParams[0].begin(),vParams[0].end()).c_str()); - //opret = CScript(vParams[0].begin(), vParams[0].end()); - return opret; + return ret; } bool makeCCopret(CScript &opret, std::vector> &vData) @@ -147,7 +150,7 @@ bool IsCCInput(CScript const& scriptSig) return true; } -bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime) +bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_t blocktime, int64_t &actualtxfee) { LOCK(mempool.cs); CCoinsView dummy; @@ -156,9 +159,10 @@ bool CheckTxFee(const CTransaction &tx, uint64_t txfee, uint32_t height, uint64_ CCoinsViewMemPool viewMemPool(pcoinsTip, mempool); view.SetBackend(viewMemPool); valuein = view.GetValueIn(height,&interest,tx,blocktime); - if ( valuein-tx.GetValueOut() > txfee ) + actualtxfee = valuein-tx.GetValueOut(); + if ( actualtxfee > txfee ) { - //fprintf(stderr, "txfee.%li vs txfee.%li\n", valuein-tx.GetValueOut(), txfee); + //fprintf(stderr, "actualtxfee.%li vs txfee.%li\n", actualtxfee, txfee); return false; } return true; diff --git a/src/cc/COptCCParams.cpp b/src/cc/COptCCParams.cpp new file mode 100644 index 000000000..41c9ba874 --- /dev/null +++ b/src/cc/COptCCParams.cpp @@ -0,0 +1,116 @@ +/*Descriptson and examples of COptCCParams class found in: + script/standard.h/cpp + class COptCCParams + +structure of data in vData payload attached to end of CCvout: + param + OP_1 + param + OP_2 ... etc until OP_16 + OP_PUSHDATA4 is the last OP code to tell things its at the end. + + taken from standard.cpp line 22: COptCCParams::COptCCParams(std::vector &vch) + +EXAMPLE taken from Verus how to create scriptPubKey from COptCCParams class: +EXAMPLE taken from Verus how to decode scriptPubKey from COptCCParams class: +*/ + +bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout) +{ + CCcontract_info *cp, C; + cp = CCinit(&C,EVAL_STAKEGUARD); + + CPubKey ccAddress = CPubKey(ParseHex(cp->CChexstr)); + + // return an output that is bound to the stake transaction and can be spent by presenting either a signed condition by the original + // destination address or a properly signed stake transaction of the same utxo on a fork + vout = MakeCC1of2vout(EVAL_STAKEGUARD, value, dest, ccAddress); + + std::vector vPubKeys = std::vector(); + vPubKeys.push_back(dest); + vPubKeys.push_back(ccAddress); + + std::vector> vData = std::vector>(); + + CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); + + hw << stakeTx.vin[0].prevout.hash; + hw << stakeTx.vin[0].prevout.n; + + uint256 utxo = hw.GetHash(); + vData.push_back(std::vector(utxo.begin(), utxo.end())); // Can we use any data here to construct vector? + + CStakeParams p; + if (GetStakeParams(stakeTx, p)) + { + // prev block hash and height is here to make validation easy + vData.push_back(std::vector(p.prevHash.begin(), p.prevHash.end())); + std::vector height = std::vector(4); + for (int i = 0; i < 4; i++) + { + height[i] = (p.blkHeight >> (8 * i)) & 0xff; + } + vData.push_back(height); + + COptCCParams ccp = COptCCParams(COptCCParams::VERSION, EVAL_STAKEGUARD, 1, 2, vPubKeys, vData); + + vout.scriptPubKey << ccp.AsVector() << OP_DROP; + return true; + } + return false; +} + +bool ValidateMatchingStake(const CTransaction &ccTx, uint32_t voutNum, const CTransaction &stakeTx, bool &cheating) +{ + // an invalid or non-matching stake transaction cannot cheat + cheating = false; + + //printf("ValidateMatchingStake: ccTx.vin[0].prevout.hash: %s, ccTx.vin[0].prevout.n: %d\n", ccTx.vin[0].prevout.hash.GetHex().c_str(), ccTx.vin[0].prevout.n); + + if (ccTx.IsCoinBase()) + { + CStakeParams p; + if (ValidateStakeTransaction(stakeTx, p)) + { + std::vector> vParams = std::vector>(); + CScript dummy; + + if (ccTx.vout[voutNum].scriptPubKey.IsPayToCryptoCondition(&dummy, vParams) && vParams.size() > 0) + { + COptCCParams ccp = COptCCParams(vParams[0]); + if (ccp.IsValid() & ccp.vData.size() >= 3 && ccp.vData[2].size() <= 4) + { + CVerusHashWriter hw = CVerusHashWriter(SER_GETHASH, PROTOCOL_VERSION); + + hw << stakeTx.vin[0].prevout.hash; + hw << stakeTx.vin[0].prevout.n; + uint256 utxo = hw.GetHash(); + + uint32_t height = 0; + int i, dataLen = ccp.vData[2].size(); + for (i = dataLen - 1; i >= 0; i--) + { + height = (height << 8) + ccp.vData[2][i]; + } + // for debugging strange issue + // printf("iterator: %d, height: %d, datalen: %d\n", i, height, dataLen); + + if (utxo == uint256(ccp.vData[0])) + { + if (p.prevHash != uint256(ccp.vData[1]) && p.blkHeight >= height) + { + cheating = true; + return true; + } + // if block height is equal and we are at the else, prevHash must have been equal + else if (p.blkHeight == height) + { + return true; + } + } + } + } + } + } + return false; +} diff --git a/src/cc/Makefile_rogue b/src/cc/Makefile_rogue index baf8767aa..3b2f65e00 100644 --- a/src/cc/Makefile_rogue +++ b/src/cc/Makefile_rogue @@ -1,6 +1,6 @@ SHELL = /bin/sh CC = gcc -CC_DARWIN = g++-6 +CC_DARWIN = g++-8 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 @@ -14,7 +14,7 @@ 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/ +#HEADERS = $(shell echo ../cryptoconditions/include/*.h) -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ all: $(TARGET) diff --git a/src/cc/crypto777/OS_portable.h b/src/cc/crypto777/OS_portable.h index d6abc98c5..aeb45bc32 100755 --- a/src/cc/crypto777/OS_portable.h +++ b/src/cc/crypto777/OS_portable.h @@ -309,8 +309,8 @@ char *uppercase_str(char *buf,char *str); char *lowercase_str(char *buf,char *str); int32_t strsearch(char *strs[],int32_t num,char *name); int32_t OS_getline(int32_t waitflag,char *line,int32_t max,char *dispstr); -int32_t sort64s(uint64_t *buf,uint32_t num,int32_t size); -int32_t revsort64s(uint64_t *buf,uint32_t num,int32_t size); +void sort64s(uint64_t *buf,uint32_t num,int32_t size); +void revsort64s(uint64_t *buf,uint32_t num,int32_t size); int decode_base32(uint8_t *token,uint8_t *tokenstr,int32_t len); int init_base32(char *tokenstr,uint8_t *token,int32_t len); char *OS_mvstr(); diff --git a/src/cc/customcc.cpp b/src/cc/customcc.cpp index a8b0bf871..f7e8e6407 100644 --- a/src/cc/customcc.cpp +++ b/src/cc/customcc.cpp @@ -82,12 +82,12 @@ UniValue custom_func1(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) 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; + CScript opret; int32_t numvout = 0; if ( has_opret(tx, EVAL_CUSTOM) == 0 ) { std::vector> vParams = std::vector>(); - opret = getCCopret(tx.vout[0].scriptPubKey); - numvout = 1; + if ( getCCopret(tx.vout[0].scriptPubKey,opret) ) + numvout = 1; } else { diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 62f020063..a23a7b16c 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -53,6 +53,15 @@ bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn) eval->state.GetRejectReason().data(), tx.vin[nIn].prevout.hash.GetHex().data()); if (eval->state.IsError()) fprintf(stderr, "Culprit: %s\n", EncodeHexTx(tx).data()); + CTransaction tmp; + if (mempool.lookup(tx.GetHash(), tmp)) + { + // This is to remove a payments airdrop if it gets stuck in the mempool. + // Miner will mine 1 invalid block, but doesnt stop them mining until a restart. + // This would almost never happen in normal use. + std::list dummy; + mempool.remove(tx,dummy,true); + } return false; } diff --git a/src/cc/hempcoin_notes.txt b/src/cc/hempcoin_notes.txt new file mode 100644 index 000000000..2fd72c897 --- /dev/null +++ b/src/cc/hempcoin_notes.txt @@ -0,0 +1,65 @@ +How this works: + - earlytxid must be a transaction included in the chain before block 100. The chain MUST not have any other of these type of tx before block 100, or someone may be able to change it and mess things up. + - When it gets to block 100, it takes the txid specified by the -earlytxid param (does not affect magic) + - Looks up the transaction searches for the opreturn, then permenantly appends it to the end of ac_script in RAM. + - After every daemon restart, the first time the daemon mines a block, or receives a block that pays ac_script it will look up the op_return and save it again. + - this enables it to always reach consensus but doesnt need to constantly keep looking up the tx in the chain. + - The trick is to use ac_founders=101 or higher so that nothing is ever paid to the unspendable CC address. Although it should still work without this it burns coins. + +-ac_script can be any Global CC address you can spend to with an OP_RETURN. Here we use example of paymentsCC being used to fund a rewards plan, and a set of founders address's. + you can get the ac_script from another chain, but the op_return payload must generated on the chain itself. this command gives you the needed info to get the scripPubKey Hex: + ./komodo-cli -ac_name=TEST paymentsfund '["5d536f54332db09f2be04593c54f764cf569e225f4d8df5155658c679e663682",1000]' + append: b8, to the end of ac_script, this changes magic value for -earlytxid chains vs normal ac_script and allows bypass of ac_supply paid to the scritpt as it would be unspendable and you would be unable to create the needed plans with no coins. + -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 + +-testnode=1 is not affecting magic and allows mining on a single node, we can use this to bootstrap the chain before syncing a second node to save time. + +start chain and make sure to do the following steps before block 100 (set generate false/true is a good idea between steps) + ./komodod -ac_name=TESTHC -ac_supply=1000000 -ac_reward=100000000000 -ac_cc=2 -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 -ac_founders=150 -ac_blocktime=20 -ac_nk=96,5 -testnode=1 + +create rewards plan and fund it with all or a % of the premine. Must be some amount. eg. + ./komodo-cli -ac_name=TESTHC rewardscreatefunding test 50000 25 0 2 500 + +do rewards add funding: + ./komodo-cli -ac_name=TESTHC rewardsaddfunding test 47a3150150bd196bd2086cae5e0c6b01a23785a04139fa660d169121a534b38e 1000 + +and get the script pubkey and op_return from this tx (no need to send it) +./komodo-cli -ac_name=TESTHC decoderawtransaction 010000000204ca4c7aaae62bb8fc9412ac010e047fa8d33c3f87d2adeb3e02170642ddfe370000000049483045022100d7b9a +4f28ca3a35f34dcdb6075e905cde1eaa962bd0619d0a8ed8e17e952bc99022077308e12325fc2a02c752ec3df9aeee1fc219ea54a4d3884834582b75c89815e01ffffffff08800132da3233d80c65e87b6db6a76dcf +188e4fdfa23198d69f647e67754cfb0000000049483045022100d6a8f7a1c4f6013f5897768ae0117fe61dfb72352d3e6652e64a6588db3ffcb102202aa1d041b24f9cbbf7028295b7c5e7f18b4f95ae39c13031dab +7f06634438e6801ffffffff0300e8764817000000302ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401ccf0c0764817000000232103bbec93af84 +0933ae2d35fc56eff24f34dbe26871402552f84c44f690945ccd79ac00000000000000002c6a2ae54174657374000000008eb334a52191160d66fa3941a08537a2016b0c5eae6c08d26b19bd500115a34700000000 + +From the return of this you need the scriptpubkey hex of vout 0: + scriptPubKey: 2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc +and the scriptpubkey hex of the OP_RETURN in vout 2. + OP_RETURN: 6a2ae54174657374000000008eb334a52191160d66fa3941a08537a2016b0c5eae6c08d26b19bd500115a347 + +create txidopreturn for this payment: + ./komodo-cli -ac_name=TESTHC paymentstxidopret '[50,"2ea22c802065686d47a4049c2c845a71895a915eb84c04445896eec5dc0be40df0b31372da8103120c008203000401cc","6a2ae54174657374000000008eb334a52191160d66fa3941a08537a2016b0c5eae6c08d26b19bd500115a347"]' + +create the txidopret for the founders reward(s) pubkeys: should be able to be a few here, not sure of max number yet. These can pay anything that does not need an opreturn. allocation and scriptpubkey hex. + ./komodo-cli -ac_name=TESTHC paymentstxidopret '[50,"76a9146bf5dd9f679c87a3f83ea176f82148d26653c04388ac"]' + +create payments plan: + ./komodo-cli -ac_name=TESTHC paymentscreate '[0,0,"61f55f2f87dad3a37d42731a8cb73b3ebea1817abfa176218162c360a8bd7145","0550014823ffa0aa99d7dd7ca5292f4dd0a1b9156eddec03412c953f095181bc"]' +gives plan txid: ee7765be874fb084c00538b1b0488e8ecb857de253f09a9ba6ea8d3579b77d33 + +paymentsfund: + To do this you first need to change the type of tx generated by paymentsfund RPC. in payments.cpp go to line: 639 and comment it out, then uncomment the block of code under this. + change the line 646 to line 647 with comments, and line 650/651 aswell. This enables the RPC to generate the ccvout opreturn payload you need without sending the payment on the chain. Just decode the raw hex. + ./komodo-cli -ac_name=TESTHC paymentsfund '["ee7765be874fb084c00538b1b0488e8ecb857de253f09a9ba6ea8d3579b77d33",1000,1]' + +get the payment fund scriptpubkey hex from vout 0: (the split it at OP_CHECKCRYPTOCONDITION or 'cc' ) + 2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401cc 2a0401f00101246a22f046337db779358deaa69b9af053e27d85cb8e8e48b0b13805c084b04f87be6577ee75 + + put the second half into an OP_RETURN: (the remaining part of the the above scriptpubkey) eg. + ./komodo-cli -ac_name=TESTHC opreturn_burn 1 2a0401f00101246a22f046337db779358deaa69b9af053e27d85cb8e8e48b0b13805c084b04f87be6577ee75 + opret_burn takes any burn amount and arbitrary hex string. (RPC works, but may have bugs, likely use this for LABS too with some fixes) + this gives a raw hex. Decode it and check the OP_RETURN is right before sending. + -earlytxid=810bd62fb8353fad20267ff2050684b8829affa3edf6b366633931530791dfce + restart the chain with earlytxid param before height 100 on all nodes (if not using -testnode=1) + ./komodod -ac_name=TESTHC -ac_supply=1000000 -ac_reward=100000000000 -ac_cc=2 -ac_script=2ea22c8020987fad30df055db6fd922c3a57e55d76601229ed3da3b31340112e773df3d0d28103120c008203000401ccb8 -ac_founders=150 -ac_blocktime=20 -ac_nk=96,5 -earlytxid=810bd62fb8353fad20267ff2050684b8829affa3edf6b366633931530791dfce + +once the payments plan has been funded with the mined coinbase you can issue payments release when conditions of the plan are met to fund founders reward/rewards plan. eg. + ./komodo-cli -ac_name=TESTHC paymentsrelease '["ee7765be874fb084c00538b1b0488e8ecb857de253f09a9ba6ea8d3579b77d33",500]' diff --git a/src/cc/import.cpp b/src/cc/import.cpp index ded10abd5..35b4f5405 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -535,6 +535,8 @@ bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction uint256 tokenid = zeroid; if (vimportOpret.begin()[0] == EVAL_TOKENS) { // for tokens (new opret with tokens) + if ( is_STAKED(ASSETCHAINS_SYMBOL) == 1 ) + return eval->Invalid("no-tokens-migrate-on-LABS"); struct CCcontract_info *cpTokens, CCtokens_info; std::vector> oprets; uint8_t evalCodeInOpret; diff --git a/src/cc/makecclib b/src/cc/makecclib index b2f8e2ee1..e4816c55c 100755 --- a/src/cc/makecclib +++ b/src/cc/makecclib @@ -4,6 +4,8 @@ rm *.so rogue/rogue games/tetris games/prices echo rogue make -f Makefile_rogue ./makerogue +rm ../libcc.so +cp librogue.so ../libcc.so 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 @@ -16,4 +18,3 @@ echo games prices echo customcc stub gcc -O3 -DBUILD_CUSTOMCC -std=c++11 -I../secp256k1/include -I../univalue/include -I../cryptoconditions/include -I../cryptoconditions/src -I../cryptoconditions/src/asn -I.. -I. -fPIC -shared -c -o customcc.so cclib.cpp - diff --git a/src/cc/makecustom b/src/cc/makecustom index 61b251e6e..154be4f31 100755 --- a/src/cc/makecustom +++ b/src/cc/makecustom @@ -4,4 +4,3 @@ cp customcc.so ../libcc.so cd .. make cd cc - diff --git a/src/cc/makerogue b/src/cc/makerogue index ff16cbb16..a389ba2dd 100755 --- a/src/cc/makerogue +++ b/src/cc/makerogue @@ -35,3 +35,6 @@ else echo ROGUE BUILD FAILED exit 1 fi + +rm ../libcc.so +cp librogue.so ../libcc.so diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index ef457fe82..f019ae271 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -92,10 +92,11 @@ vout.n-1: opreturn with oracletxid, prevbatontxid and data in proper format */ +extern int32_t komodo_currentheight(); +#define PUBKEY_SPOOFING_FIX_ACTIVATION 1563148800 +#define CC_MARKER_VALUE 10000 // start of consensus code - - CScript EncodeOraclesCreateOpRet(uint8_t funcid,std::string name,std::string description,std::string format) { CScript opret; uint8_t evalcode = EVAL_ORACLES; @@ -124,6 +125,7 @@ uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,s CScript EncodeOraclesOpRet(uint8_t funcid,uint256 oracletxid,CPubKey pk,int64_t num) { CScript opret; uint8_t evalcode = EVAL_ORACLES; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << oracletxid << pk << num); return(opret); } @@ -131,11 +133,12 @@ CScript EncodeOraclesOpRet(uint8_t funcid,uint256 oracletxid,CPubKey pk,int64_t uint8_t DecodeOraclesOpRet(const CScript &scriptPubKey,uint256 &oracletxid,CPubKey &pk,int64_t &num) { std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey,vopret); script = (uint8_t *)vopret.data(); if ( vopret.size() > 2 && script[0] == EVAL_ORACLES ) { - if (script[0] == EVAL_ORACLES && (script[1]== 'R' || script[1] == 'S') && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> oracletxid; ss >> pk; ss >> num)!=0) + if (script[0] == EVAL_ORACLES && (script[1]== 'R' || script[1] == 'S' || script[1] == 'F') && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> oracletxid; ss >> pk; ss >> num)!=0) return(f); else return(script[1]); } @@ -633,9 +636,25 @@ bool OraclesDataValidate(struct CCcontract_info *cp,Eval* eval,const CTransactio else return(true); } +int32_t GetLatestTimestamp(int32_t height) +{ + uint256 blockHash; CBlock block; CBlockIndex* pindex; + + blockHash = chainActive[height]->GetBlockHash(); + pindex = mapBlockIndex[blockHash]; + if ( komodo_blockload(block,pindex) == 0 ) + { + return(block.nTime); + } + return(0); +} + bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - uint256 txid,oracletxid,batontxid; uint64_t txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts; uint8_t *script; std::vector vopret,data; CScript scriptPubKey; CPubKey publisher; + uint256 oracletxid,batontxid; uint64_t txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts; int64_t amount; uint256 hashblock; + uint8_t *script; std::vector vopret,data; CPubKey publisher,tmppk,oraclespk; char tmpaddress[64],vinaddress[64],oraclesaddr[64]; + CTransaction tmptx; std::string name,desc,format; + numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -643,10 +662,11 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t return eval->Invalid("no vouts"); else { - txid = tx.GetHash(); GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() > 2 ) - { + { + oraclespk=GetUnspendable(cp,0); + Getscriptaddress(oraclesaddr,CScript() << ParseHex(HexStr(oraclespk)) << OP_CHECKSIG); script = (uint8_t *)vopret.data(); switch ( script[1] ) { @@ -657,13 +677,45 @@ bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t // vout.n-1: opreturn with name and description and format for data return eval->Invalid("unexpected OraclesValidate for create"); break; - case 'R': // register + case 'F': // fund (activation on Jul 15th 2019 00:00) // vins.*: normal inputs + // vout.0: txfee to oracle CC address of users pubkey + // vout.1: change, if any + // vout.n-1: opreturn with createtxid, pubkey and amount + return eval->Invalid("unexpected OraclesValidate for create"); + break; + case 'R': // register + // vin.0: normal inputs + // vin.n-1: CC input from pubkeys oracle CC addres - to prove that register came from pubkey that is registred (activation on Jul 15th 2019 00:00) // vout.0: txfee tag to normal marker address // vout.1: baton CC utxo - // vout.2: change, if any + // vout.2: marker from oraclesfund tx to normal pubkey address (activation on Jul 15th 2019 00:00) + // vout.n-2: change, if any // vout.n-1: opreturn with createtxid, pubkey and price per data point - return eval->Invalid("unexpected OraclesValidate for register"); + if (GetLatestTimestamp(eval->GetCurrentHeight())>PUBKEY_SPOOFING_FIX_ACTIVATION) + { + if ((numvouts=tx.vout.size()) < 1 || DecodeOraclesOpRet(tx.vout[numvouts-1].scriptPubKey,oracletxid,tmppk,amount)!='R') + return eval->Invalid("invalid oraclesregister OP_RETURN data!"); + else if (myGetTransaction(oracletxid,tmptx,hashblock) == 0) + return eval->Invalid("invalid oraclescreate txid!"); + else if ((numvouts=tmptx.vout.size()) < 1 || DecodeOraclesCreateOpRet(tmptx.vout[numvouts-1].scriptPubKey,name,desc,format)!='C') + return eval->Invalid("invalid oraclescreate OP_RETURN data!"); + else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for oraclescreate!"); + else if (ConstrainVout(tmptx.vout[0],0,oraclesaddr,txfee)==0) + return eval->Invalid("invalid marker for oraclescreate!"); + else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for oraclesregister!"); + else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 + || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE || !Getscriptaddress(vinaddress,tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].scriptPubKey) + || !GetCCaddress(cp,tmpaddress,tmppk) || strcmp(tmpaddress,vinaddress)!=0) + return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC for oraclesregister or pubkey not same as vin pubkey, register must be done from owner of pubkey that registers to oracle!!"); + else if (CCtxidaddr(tmpaddress,oracletxid).IsValid() && ConstrainVout(tx.vout[0],0,tmpaddress,txfee)==0) + return eval->Invalid("invalid marker for oraclesregister!"); + else if (!Getscriptaddress(tmpaddress,CScript() << ParseHex(HexStr(tmppk)) << OP_CHECKSIG) || ConstrainVout(tx.vout[2],0,tmpaddress,CC_MARKER_VALUE)==0) + return eval->Invalid("pubkey in OP_RETURN and in vout.2 not matching, register must be done from owner of pubkey that registers to oracle!"); + } + else return eval->Invalid("unexpected OraclesValidate for register"); break; case 'S': // subscribe // vins.*: normal inputs @@ -762,6 +814,45 @@ int64_t LifetimeOraclesFunds(struct CCcontract_info *cp,uint256 oracletxid,CPubK return(total); } +int64_t AddMyOraclesFunds(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint256 oracletxid) +{ + char coinaddr[64],funcid; int64_t nValue,tmpamount; uint256 tmporacletxid,txid,hashBlock,ignoretxid; int32_t numvouts,vout,ignorevin; + std::vector > unspentOutputs; CTransaction vintx; CPubKey tmppk; + + GetCCaddress(cp,coinaddr,pk); + SetCCunspents(unspentOutputs,coinaddr,true); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + nValue = it->second.satoshis; + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && (numvouts=vintx.vout.size())>0) + { + if ((funcid=DecodeOraclesOpRet(vintx.vout[numvouts-1].scriptPubKey,tmporacletxid,tmppk,tmpamount))!=0 && funcid=='F' && tmppk==pk + && tmporacletxid==oracletxid && tmpamount==nValue && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout)==0) + { + mtx.vin.push_back(CTxIn(txid,vout,CScript())); + return (nValue); + } + } else fprintf(stderr,"couldnt find transaction\n"); + } + + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) + { + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + nValue=txmempool.vout[0].nValue; + + if ((funcid=DecodeOraclesOpRet(txmempool.vout[txmempool.vout.size()-1].scriptPubKey,tmporacletxid,tmppk,tmpamount))!=0 && funcid=='F' + && tmppk==pk && tmporacletxid==oracletxid && tmpamount==nValue && myIsutxo_spentinmempool(ignoretxid,ignorevin,hash,0)==0) + { + mtx.vin.push_back(CTxIn(hash,0,CScript())); + return (nValue); + } + } + return (0); +} + std::string OracleCreate(int64_t txfee,std::string name,std::string description,std::string format) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -802,10 +893,36 @@ std::string OracleCreate(int64_t txfee,std::string name,std::string description, return(""); } +std::string OracleFund(int64_t txfee,uint256 oracletxid) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CPubKey mypk,oraclespk; struct CCcontract_info *cp,C; + + if (GetLatestTimestamp(komodo_currentheight())evalcode,CC_MARKER_VALUE,mypk)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('F',oracletxid,mypk,CC_MARKER_VALUE))); + } + CCerror = strprintf("error adding normal inputs"); + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); +} + std::string OracleRegister(int64_t txfee,uint256 oracletxid,int64_t datafee) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey mypk,markerpubkey,batonpk; struct CCcontract_info *cp,C; char markeraddr[64],batonaddr[64]; + CPubKey mypk,markerpubkey,batonpk,oraclespk; struct CCcontract_info *cp,C; char markeraddr[64],batonaddr[64]; + cp = CCinit(&C,EVAL_ORACLES); if ( txfee == 0 ) txfee = 10000; @@ -816,12 +933,20 @@ std::string OracleRegister(int64_t txfee,uint256 oracletxid,int64_t datafee) return(""); } mypk = pubkey2pk(Mypubkey()); + oraclespk = GetUnspendable(cp,0); batonpk = OracleBatonPk(batonaddr,cp); markerpubkey = CCtxidaddr(markeraddr,oracletxid); - if ( AddNormalinputs(mtx,mypk,3*txfee,4) > 0 ) + if (AddNormalinputs(mtx,mypk,3*txfee,4)) { + if (GetLatestTimestamp(komodo_currentheight())>PUBKEY_SPOOFING_FIX_ACTIVATION && AddMyOraclesFunds(cp,mtx,mypk,oracletxid)!=CC_MARKER_VALUE) + { + CCerror = strprintf("error adding inputs from your Oracles CC address, please fund it first with oraclesfund rpc!"); + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + } mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(markerpubkey)) << OP_CHECKSIG)); mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,batonpk)); + if (GetLatestTimestamp(komodo_currentheight())>PUBKEY_SPOOFING_FIX_ACTIVATION) mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('R',oracletxid,mypk,datafee))); } CCerror = strprintf("error adding normal inputs"); @@ -929,50 +1054,81 @@ UniValue OracleFormat(uint8_t *data,int32_t datalen,char *format,int32_t formatl { str[0] = 0; j = oracle_format(&hash,&val,str,format[i],data,j,datalen); + obj.push_back(str); if ( j < 0 ) break; - obj.push_back(str); if ( j >= datalen ) break; } return(obj); } -UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num) +UniValue OracleDataSamples(uint256 reforacletxid,char* batonaddr,int32_t num) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; - CPubKey pk; std::string name,description,format; int32_t numvouts,n=0; std::vector data; char *formatstr = 0; + UniValue result(UniValue::VOBJ),b(UniValue::VARR); CTransaction tx,oracletx; uint256 txid,hashBlock,btxid,oracletxid; + CPubKey pk; std::string name,description,format; int32_t numvouts,n=0,vout; std::vector data; char *formatstr = 0; + std::vector > addressIndex; int64_t nValue; result.push_back(Pair("result","success")); if ( GetTransaction(reforacletxid,oracletx,hashBlock,false) != 0 && (numvouts=oracletx.vout.size()) > 0 ) { if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) == 'C' ) { - while ( GetTransaction(batontxid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size()) > 0 ) + BOOST_FOREACH(const CTxMemPoolEntry &e, mempool.mapTx) { - if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) + const CTransaction &txmempool = e.GetTx(); + const uint256 &hash = txmempool.GetHash(); + if ((numvouts=txmempool.vout.size())>0 && DecodeOraclesData(txmempool.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) { if ( (formatstr= (char *)format.c_str()) == 0 ) formatstr = (char *)""; - a.push_back(OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size())); - batontxid = btxid; - if ( ++n >= num ) + UniValue a(UniValue::VOBJ); + a.push_back(Pair("txid",hash.GetHex())); + a.push_back(Pair("data",OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size()))); + b.push_back(a); + if ( ++n >= num && num != 0) break; - } else break; + } + } + SetCCtxids(addressIndex,batonaddr,true); + if (addressIndex.size()>0) + { + for (std::vector >::const_iterator it=addressIndex.end()-1; it!=addressIndex.begin(); it--) + { + txid=it->first.txhash; + vout = (int32_t)it->first.index; + nValue = (int64_t)it->second; + if (vout==1 && nValue==10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts=tx.vout.size()) > 0 ) + { + if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,btxid,pk,data) == 'D' && reforacletxid == oracletxid ) + { + if ( (formatstr= (char *)format.c_str()) == 0 ) + formatstr = (char *)""; + UniValue a(UniValue::VOBJ); + a.push_back(Pair("txid",txid.GetHex())); + a.push_back(Pair("data",OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size()))); + b.push_back(a); + if ( ++n >= num && num != 0) + break; + } + } + } } } } - result.push_back(Pair("samples",a)); + result.push_back(Pair("samples",b)); return(result); } UniValue OracleInfo(uint256 origtxid) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); - std::vector > unspentOutputs; + std::vector > unspentOutputs; int32_t height; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction regtx,tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; + CTransaction tx; std::string name,description,format; uint256 hashBlock,txid,oracletxid,batontxid; CPubKey pk; struct CCcontract_info *cp,C; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64],batonaddr[64]; std::vector data; + std::map> publishers; + cp = CCinit(&C,EVAL_ORACLES); CCtxidaddr(markeraddr,origtxid); if ( GetTransaction(origtxid,tx,hashBlock,false) == 0 ) @@ -996,28 +1152,38 @@ UniValue OracleInfo(uint256 origtxid) for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; - if ( GetTransaction(txid,regtx,hashBlock,false) != 0 ) + height = (int32_t)it->second.blockHeight; + if ( GetTransaction(txid,tx,hashBlock,false) != 0 && tx.vout.size() > 0 && + DecodeOraclesOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == origtxid ) { - if ( regtx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == origtxid ) + if (publishers.find(pk)==publishers.end() || height>publishers[pk].second) { - UniValue obj(UniValue::VOBJ); - obj.push_back(Pair("publisher",pubkey33_str(str,(uint8_t *)pk.begin()))); - Getscriptaddress(batonaddr,regtx.vout[1].scriptPubKey); - batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); - obj.push_back(Pair("baton",batonaddr)); - obj.push_back(Pair("batontxid",uint256_str(str,batontxid))); - funding = LifetimeOraclesFunds(cp,oracletxid,pk); - sprintf(numstr,"%.8f",(double)funding/COIN); - obj.push_back(Pair("lifetime",numstr)); - funding = AddOracleInputs(cp,mtx,oracletxid,pk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - obj.push_back(Pair("funds",numstr)); - sprintf(numstr,"%.8f",(double)datafee/COIN); - obj.push_back(Pair("datafee",numstr)); - a.push_back(obj); + publishers[pk].first=txid; + publishers[pk].second=height; } } } + for (std::map>::iterator it = publishers.begin(); it != publishers.end(); ++it) + { + if ( GetTransaction(it->second.first,tx,hashBlock,false) != 0 && DecodeOraclesOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R') + { + UniValue obj(UniValue::VOBJ); + obj.push_back(Pair("publisher",pubkey33_str(str,(uint8_t *)pk.begin()))); + Getscriptaddress(batonaddr,tx.vout[1].scriptPubKey); + batontxid = OracleBatonUtxo(10000,cp,oracletxid,batonaddr,pk,data); + obj.push_back(Pair("baton",batonaddr)); + obj.push_back(Pair("batontxid",uint256_str(str,batontxid))); + funding = LifetimeOraclesFunds(cp,oracletxid,pk); + sprintf(numstr,"%.8f",(double)funding/COIN); + obj.push_back(Pair("lifetime",numstr)); + funding = AddOracleInputs(cp,mtx,oracletxid,pk,0,0); + sprintf(numstr,"%.8f",(double)funding/COIN); + obj.push_back(Pair("funds",numstr)); + sprintf(numstr,"%.8f",(double)datafee/COIN); + obj.push_back(Pair("datafee",numstr)); + a.push_back(obj); + } + } result.push_back(Pair("registered",a)); } } diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 8bd41b333..b6e85480c 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -16,12 +16,6 @@ #include "CCPayments.h" /* - --earlytxid is not an -ac_param, so it doesnt affect the chain magics -extra data after the normal CCvout is whatever data we want and can represent whatever we want -so -ac_script= -in the validation if you see the useearlytxid in the opreturn data or extra data, you use the earlytxid as the txid that specifies the payment - 0) txidopret <- allocation, scriptPubKey, opret 1) create <- locked_blocks, minrelease, list of txidopret @@ -123,6 +117,46 @@ uint8_t DecodePaymentsFundOpRet(CScript scriptPubKey,uint256 &checktxid) return(0); } +CScript EncodePaymentsMergeOpRet(uint256 checktxid) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'M' << checktxid); + return(opret); +} + +uint8_t DecodePaymentsMergeOpRet(CScript scriptPubKey,uint256 &checktxid) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> checktxid) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'M' ) + return(f); + } + return(0); +} + +CScript EncodePaymentsReleaseOpRet(uint256 checktxid, int64_t amountReleased) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'R' << checktxid << amountReleased); + return(opret); +} + +uint8_t DecodePaymentsReleaseOpRet(CScript scriptPubKey,uint256 &checktxid,int64_t &amountReleased) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> checktxid; ss >> amountReleased) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'R' ) + return(f); + } + return(0); +} + CScript EncodePaymentsOpRet(int32_t lockedblocks,int32_t minrelease,int64_t totalallocations,std::vector txidoprets) { CScript opret; uint8_t evalcode = EVAL_PAYMENTS; @@ -137,16 +171,56 @@ uint8_t DecodePaymentsOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t & script = (uint8_t *)vopret.data(); if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> totalallocations; ss >> txidoprets) != 0 ) { - if ( e == EVAL_PAYMENTS && f == 'C' ) + if ( e == EVAL_PAYMENTS && f == 'C' && txidoprets.size() > 1 ) return(f); } return(0); } -int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr) +CScript EncodePaymentsSnapsShotOpRet(int32_t lockedblocks,int32_t minrelease,int32_t minimum,int32_t top,int32_t bottom,int8_t fixedAmount,std::vector> excludeScriptPubKeys) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'S' << lockedblocks << minrelease << minimum << top << bottom << fixedAmount << excludeScriptPubKeys); + return(opret); +} + +uint8_t DecodePaymentsSnapsShotOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &minimum,int32_t &top,int32_t &bottom,int8_t &fixedAmount,std::vector> &excludeScriptPubKeys) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minrelease; ss >> minimum; ss >> top; ; ss >> bottom; ss >> fixedAmount; ss >> excludeScriptPubKeys) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'S' ) + return(f); + } + return(0); +} + +CScript EncodePaymentsTokensOpRet(int32_t lockedblocks,int32_t minrelease,int32_t minimum,int32_t top,int32_t bottom,int8_t fixedAmount,std::vector> excludeScriptPubKeys,uint256 tokenid) +{ + CScript opret; uint8_t evalcode = EVAL_PAYMENTS; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'O' << lockedblocks << minrelease << minimum << top << bottom << fixedAmount << excludeScriptPubKeys << tokenid); + return(opret); +} + +uint8_t DecodePaymentsTokensOpRet(CScript scriptPubKey,int32_t &lockedblocks,int32_t &minrelease,int32_t &minimum,int32_t &top,int32_t &bottom,int8_t &fixedAmount,std::vector> &excludeScriptPubKeys, uint256 &tokenid) +{ + std::vector vopret; uint8_t *script,e,f; + GetOpReturnData(scriptPubKey, vopret); + script = (uint8_t *)vopret.data(); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> lockedblocks; ss >> minimum; ss >> top; ; ss >> bottom; ss >> fixedAmount; ss >> excludeScriptPubKeys; ss >> tokenid) != 0 ) + { + if ( e == EVAL_PAYMENTS && f == 'O' ) + return(f); + } + return(0); +} + +int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,char *cmpaddr, CScript &ccopret) { char destaddr[64]; - if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) + if ( getCCopret(tx.vout[v].scriptPubKey, ccopret) ) { if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (cmpaddr[0] == 0 || strcmp(destaddr,cmpaddr) == 0) ) return(tx.vout[v].nValue); @@ -154,195 +228,326 @@ int64_t IsPaymentsvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t return(0); } -void pub2createtxid(char *str) +bool payments_game(int32_t &top, int32_t &bottom) { - int i,n; - char *rev; - n = (int32_t)strlen(str); - rev = (char *)malloc(n + 1); - for (i=0; iGetBlockHash(); + memcpy(&x,&tmphash,sizeof(x)); + bottom = ((x & 0xff) % 50); + if ( bottom == 0 ) bottom = 1; + top = (((x>>8) & 0xff) % 100); + if ( top < 50 ) top += 50; + bottom = (vAddressSnapshot.size()*bottom)/100; + top = (vAddressSnapshot.size()*top)/100; + //fprintf(stderr, "bottom.%i top.%i\n",bottom,top); + return true; +} + +bool payments_lockedblocks(uint256 blockhash,int32_t lockedblocks,int32_t &blocksleft) +{ + int32_t ht = chainActive.Height(); + CBlockIndex* pblockindex = komodo_blockindex(blockhash); + if ( pblockindex == 0 || pblockindex->GetHeight()+lockedblocks > ht) { - rev[n-2-i] = str[i]; - rev[n-1-i] = str[i+1]; + blocksleft = pblockindex->GetHeight()+lockedblocks - ht; + fprintf(stderr, "not elegible to be spent yet height.%i vs elegible_ht.%i blocksleft.%i\n",ht,(pblockindex!=0?pblockindex->GetHeight():0)+lockedblocks,blocksleft); + return false; } - rev[n] = 0; - strcpy(str,rev); - free(rev); + return true; +} + +int32_t payments_getallocations(int32_t top, int32_t bottom, const std::vector> &excludeScriptPubKeys, mpz_t &mpzTotalAllocations, std::vector &scriptPubKeys, std::vector &allocations) +{ + mpz_t mpzAllocation; int32_t i =0; + for (int32_t j = bottom; j < vAddressSnapshot.size(); j++) + { + auto &address = vAddressSnapshot[j]; + CScript scriptPubKey = GetScriptForDestination(address.second); + bool skip = false; + // skip excluded addresses. + for ( auto skipkey : excludeScriptPubKeys ) + { + if ( scriptPubKey == CScript(skipkey.begin(), skipkey.end()) ) + skip = true; + } + if ( !skip ) + { + mpz_init(mpzAllocation); + i++; + //fprintf(stderr, "address: %s nValue.%li \n", CBitcoinAddress(address.second).ToString().c_str(), address.first); + scriptPubKeys.push_back(scriptPubKey); + allocations.push_back(address.first); + mpz_set_si(mpzAllocation,address.first); + mpz_add(mpzTotalAllocations,mpzTotalAllocations,mpzAllocation); + mpz_clear(mpzAllocation); + } + if ( i+bottom == top ) + break; // we reached top amount to pay, it can be less than this, if less address exist on chain, return the number we got. + } + return(i); +} + +int32_t payments_gettokenallocations(int32_t top, int32_t bottom, const std::vector> &excludeScriptPubKeys, uint256 tokenid, mpz_t &mpzTotalAllocations, std::vector &scriptPubKeys, std::vector &allocations) +{ + /* + - check tokenid exists. + - iterate tokenid address and extract all pubkeys, add to map. + - rewind to last notarized height for balances? see main.cpp: line# 660. + - convert balances to mpz_t and add up totalallocations + - sort the map into a vector, then convert to the correct output. + */ + return(0); } bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - // one of two addresses - // change must go to 1of2 txidaddr - // change is/must be in vout[0] - // only 'F' or 1of2 txidaddr can be spent - // all vouts must match exactly - char temp[128], coinaddr[64], txidaddr[64]; std::string scriptpubkey; uint256 createtxid, blockhash; CTransaction tmptx; - int32_t i,lockedblocks,minrelease; int64_t change,totalallocations; std::vector txidoprets; bool fHasOpret = false; CPubKey txidpk,Paymentspk; - // user marker vout to get the createtxid - if ( tx.vout.size() < 2 ) - return(eval->Invalid("not enough vouts")); - if ( tx.vout.back().scriptPubKey[0] == OP_RETURN ) + char temp[128], txidaddr[64]={0}; std::string scriptpubkey; uint256 createtxid, blockhash, tokenid; CTransaction plantx; int8_t funcid=0, fixedAmount=0; + int32_t i,lockedblocks,minrelease,blocksleft,dust = 0, top,bottom=0,minimum=10000; int64_t change,totalallocations,actualtxfee,amountReleased=0; std::vector txidoprets; bool fHasOpret = false,fIsMerge = false; CPubKey txidpk,Paymentspk; + std::vector> excludeScriptPubKeys; bool fFixedAmount = false; CScript ccopret; + mpz_t mpzTotalAllocations,mpzAllocation,mpzCheckamount; + mpz_init(mpzCheckamount); mpz_init(mpzTotalAllocations); + // Check change is in vout[0], and also fetch the ccopret to determine what type of tx this is. txidaddr is unknown, recheck this later. + if ( (change= IsPaymentsvout(cp,tx,0,txidaddr,ccopret)) != 0 && ccopret.size() > 2 ) { - scriptpubkey = HexStr(tx.vout[tx.vout.size()-2].scriptPubKey.begin()+2, tx.vout[tx.vout.size()-2].scriptPubKey.end()-1); - fHasOpret = true; - } else scriptpubkey = HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin()+2,tx.vout[tx.vout.size()-1].scriptPubKey.end()-1); - strcpy(temp, scriptpubkey.c_str()); - pub2createtxid(temp); - createtxid = Parseuint256(temp); - //printf("createtxid.%s\n",createtxid.ToString().c_str()); + // get the checktxid and the amount released if doing release tx. + if ( DecodePaymentsMergeOpRet(ccopret,createtxid) == 'M' ) + fIsMerge = true; + else if ( DecodePaymentsReleaseOpRet(ccopret,createtxid,amountReleased) != 'R' ) + return(eval->Invalid("could not decode ccopret")); + mpz_set_si(mpzCheckamount,amountReleased); + } else return(eval->Invalid("could not decode ccopret")); // use the createtxid to fetch the tx and all of the plans info. - if ( myGetTransaction(createtxid,tmptx,blockhash) != 0 ) - { - if ( tmptx.vout.size() > 0 && DecodePaymentsOpRet(tmptx.vout[tmptx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( myGetTransaction(createtxid,plantx,blockhash) != 0 && plantx.vout.size() > 0 ) + { + if ( ((funcid= DecodePaymentsOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(plantx.vout[plantx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid)) == 'O') ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) - return(eval->Invalid("negative values")); + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) + return(eval->Invalid("negative values")); + if ( minimum < 10000 ) + return(eval->Invalid("minimum must be over 10000")); Paymentspk = GetUnspendable(cp,0); + txidpk = CCtxidaddr(txidaddr,createtxid); + GetCCaddress1of2(cp,txidaddr,Paymentspk,txidpk); //fprintf(stderr, "lockedblocks.%i minrelease.%i totalallocations.%i txidopret1.%s txidopret2.%s\n",lockedblocks, minrelease, totalallocations, txidoprets[0].ToString().c_str(), txidoprets[1].ToString().c_str() ); - if ( !CheckTxFee(tx, PAYMENTS_TXFEE+1, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) + if ( !CheckTxFee(tx, PAYMENTS_TXFEE+1, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime, actualtxfee) ) return eval->Invalid("txfee is too high"); - // Get all the script pubkeys and allocations - std::vector allocations; - std::vector scriptPubKeys; - int64_t checkallocations = 0; - i = 0; - BOOST_FOREACH(const uint256& txidopret, txidoprets) + // Check that the change vout is playing the txid address. + if ( IsPaymentsvout(cp,tx,0,txidaddr,ccopret) == 0 ) + return eval->Invalid("change pays wrong address"); + + if ( !fIsMerge ) { - CTransaction tx0; std::vector scriptPubKey,opret; int64_t allocation; - if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + if ( amountReleased < minrelease*COIN ) { - scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); - allocations.push_back(allocation); - //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); - checkallocations += allocation; - // if we have an op_return to pay to need to check it exists and is paying the correct opret. - if ( !opret.empty() ) + fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amountReleased, (int64_t)minrelease*COIN); + return(eval->Invalid("amount is too small")); + } + // Get all the script pubkeys and allocations + std::vector allocations; + std::vector scriptPubKeys; + i = 0; + if ( funcid == 'C' ) + { + // normal payment + int64_t checkallocations = 0; + for ( auto txidopret : txidoprets) { - if ( !fHasOpret ) + CTransaction tx0; std::vector scriptPubKey,opret; int64_t allocation; + if ( myGetTransaction(txidopret,tx0,blockhash) != 0 && tx0.vout.size() > 1 && DecodePaymentsTxidOpRet(tx0.vout[tx0.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { - fprintf(stderr, "missing opret.%s in payments release.\n",HexStr(opret.begin(), opret.end()).c_str()); - return(eval->Invalid("missing opret in payments release")); - } - else if ( CScript(opret.begin(),opret.end()) != tx.vout[tx.vout.size()-1].scriptPubKey ) - { - fprintf(stderr, "opret.%s vs opret.%s\n",HexStr(opret.begin(), opret.end()).c_str(), HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin(), tx.vout[tx.vout.size()-1].scriptPubKey.end()).c_str()); - return(eval->Invalid("pays incorrect opret")); + scriptPubKeys.push_back(CScript(scriptPubKey.begin(), scriptPubKey.end())); + allocations.push_back(allocation); + //fprintf(stderr, "i.%i scriptpubkey.%s allocation.%li\n",i,scriptPubKeys[i].ToString().c_str(),allocation); + checkallocations += allocation; + // if we have an op_return to pay to need to check it exists and is paying the correct opret. + if ( !opret.empty() ) + { + if ( !fHasOpret ) + { + fprintf(stderr, "missing opret.%s in payments release.\n",HexStr(opret.begin(), opret.end()).c_str()); + return(eval->Invalid("missing opret in payments release")); + } + else if ( CScript(opret.begin(),opret.end()) != tx.vout[tx.vout.size()-1].scriptPubKey ) + { + fprintf(stderr, "opret.%s vs opret.%s\n",HexStr(opret.begin(), opret.end()).c_str(), HexStr(tx.vout[tx.vout.size()-1].scriptPubKey.begin(), tx.vout[tx.vout.size()-1].scriptPubKey.end()).c_str()); + return(eval->Invalid("pays incorrect opret")); + } + } } + i++; + } + //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); + if ( totalallocations != checkallocations ) + return(eval->Invalid("allocation missmatch")); + mpz_set_si(mpzTotalAllocations,totalallocations); + } + else if ( funcid == 'S' || funcid == 'O' ) + { + // snapshot payment + if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) + return(eval->Invalid("snapshots not activated on this chain")); + if ( vAddressSnapshot.size() == 0 ) + return(eval->Invalid("need first snapshot")); + if ( top > 3999 ) + return(eval->Invalid("transaction too big")); + if ( fixedAmount == 7 ) + { + // game setting, randomise bottom and top values + fFixedAmount = payments_game(top,bottom); + } + else if ( fixedAmount != 0 ) + { + fFixedAmount = true; + } + if ( funcid == 'S' ) + payments_getallocations(top, bottom, excludeScriptPubKeys, mpzTotalAllocations, scriptPubKeys, allocations); + else + { + // token snapshot + // payments_gettokenallocations(top, bottom, excludeScriptPubKeys, tokenid, mpzTotalAllocations, scriptPubKeys, allocations); + return(eval->Invalid("tokens not yet implemented")); } } - i++; - } - - // sanity check to make sure we got all the required info - if ( allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size() ) - return(eval->Invalid("missing data cannot validate")); - - //fprintf(stderr, "totalallocations.%li checkallocations.%li\n",totalallocations, checkallocations); - if ( totalallocations != checkallocations ) - return(eval->Invalid("allocation missmatch")); - - txidpk = CCtxidaddr(txidaddr,createtxid); - GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); - //fprintf(stderr, "coinaddr.%s\n", coinaddr); - - // make sure change is in vout 0 and is paying to the contract address. - if ( (change= IsPaymentsvout(cp,tx,0,coinaddr)) == 0 ) - return(eval->Invalid("change is in wrong vout or is wrong tx type")); - - // Check vouts go to the right place and pay the right amounts. - int64_t amount = 0, checkamount; int32_t n = 0; - checkamount = tx.GetValueOut() - change - PAYMENTS_TXFEE; - for (i = 1; i < (fHasOpret ? tx.vout.size()-2 : tx.vout.size()-1); i++) - { - std::string destscriptPubKey = HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()); - std::string voutscriptPubKey = HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()); - if ( destscriptPubKey != voutscriptPubKey ) + // sanity check to make sure we got all the required info, skip for merge type tx + //fprintf(stderr, " allocations.size().%li scriptPubKeys.size.%li\n",allocations.size(), scriptPubKeys.size()); + if ( (allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size()) ) + return(eval->Invalid("missing data cannot validate")); + + // Check vouts go to the right place and pay the right amounts. + int64_t amount = 0; int32_t n = 0; + // We place amount released into ccopret, so that these calcualtion are accurate! + // If you change the amount released in the RPC these calcs will be wrong, and validation will fail. + for (i = 1; i < (fHasOpret ? tx.vout.size()-1 : tx.vout.size()); i++) { - fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", destscriptPubKey.c_str(), voutscriptPubKey.c_str()); - return(eval->Invalid("pays wrong address")); + int64_t test; + if ( scriptPubKeys[n] != tx.vout[i].scriptPubKey ) + { + fprintf(stderr, "pays wrong destination destscriptPubKey.%s voutscriptPubKey.%s\n", HexStr(scriptPubKeys[n].begin(),scriptPubKeys[n].end()).c_str(), HexStr(tx.vout[i].scriptPubKey.begin(),tx.vout[i].scriptPubKey.end()).c_str()); + return(eval->Invalid("pays wrong address")); + } + if ( fFixedAmount ) + { + if ( (top-bottom) > 0 ) + test = amountReleased / (top-bottom); + else + return(eval->Invalid("top/bottom range is illegal")); + } + else + { + mpz_init(mpzAllocation); + mpz_set_si(mpzAllocation,allocations[n]); + mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); + mpz_tdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); + test = mpz_get_si(mpzAllocation); + mpz_clear(mpzAllocation); + } + //fprintf(stderr, "vout %i test.%li nValue.%li\n", i, test, tx.vout[i].nValue); + if ( test != tx.vout[i].nValue ) + { + fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); + return(eval->Invalid("amounts do not match")); + } + if ( test < minimum ) + { + // prevent anyone being paid the minimum. + fprintf(stderr, "vout.%i test.%li vs minimum.%i\n",i, test, minimum); + return(eval->Invalid("under minimum size")); + } + amount += tx.vout[i].nValue; + n++; } - int64_t test = allocations[n]; - test *= checkamount; - test /= totalallocations; - if ( test != tx.vout[i].nValue && test != tx.vout[i].nValue-1 ) + if ( allocations.size() > n ) { - fprintf(stderr, "vout.%i test.%li vs nVlaue.%li\n",i, test, tx.vout[i].nValue); - return(eval->Invalid("amounts do not match")); + // need to check that the next allocation was less than minimum, otherwise ppl can truncate the tx at any place not paying all elegible addresses. + mpz_init(mpzAllocation); + mpz_set_si(mpzAllocation,allocations[n+1]); + mpz_mul(mpzAllocation,mpzAllocation,mpzCheckamount); + mpz_tdiv_q(mpzAllocation,mpzAllocation,mpzTotalAllocations); + int64_t test = mpz_get_si(mpzAllocation); + //fprintf(stderr, "check next vout pays under min: test.%li > minimuim.%i\n", test, minimum); + if ( test > minimum ) + return(eval->Invalid("next allocation was not under minimum")); } - amount += tx.vout[i].nValue; - n++; + mpz_clear(mpzTotalAllocations); mpz_clear(mpzCheckamount); } - // This is a backup check to make sure there are no extra vouts paying something else! - if ( checkamount != amount ) - return(eval->Invalid("amounts do not match")); - - if ( amount < minrelease*COIN ) - { - fprintf(stderr, "does not meet minrelease amount.%li minrelease.%li\n",amount, (int64_t)minrelease*COIN ); - return(eval->Invalid("amount is too small")); - } - + // Check vins i = 0; - int32_t ht = chainActive.LastTip()->GetHeight(); - BOOST_FOREACH(const CTxIn& vin, tx.vin) + for (auto vin : tx.vin) { - CTransaction txin; + CTransaction txin; if ( myGetTransaction(vin.prevout.hash,txin,blockhash) ) { // check the vin comes from the CC address's - char destaddr[64]; - Getscriptaddress(destaddr,txin.vout[vin.prevout.n].scriptPubKey); - if ( strcmp(destaddr,coinaddr) != 0 ) + char fromaddr[64]; int32_t mergeoffset = 0; CScript vinccopret; uint256 checktxid; + Getscriptaddress(fromaddr,txin.vout[vin.prevout.n].scriptPubKey); + if ( fIsMerge && txin.vout[vin.prevout.n].nValue < COIN ) + dust++; + if ( IsPaymentsvout(cp,txin,vin.prevout.n,cp->unspendableCCaddr,vinccopret) != 0 ) { - CScript opret; uint256 checktxid; int32_t opret_ind; - if ( (opret_ind= has_opret(txin, EVAL_PAYMENTS)) == 0 ) + // if from global payments address get ccopret to detemine pays correct plan. + uint256 checktxid; + if ( vinccopret.size() < 2 || DecodePaymentsFundOpRet(vinccopret,checktxid) != 'F' || checktxid != createtxid ) { - // get op_return from CCvout - opret = getCCopret(txin.vout[0].scriptPubKey); - } - else - { - // get op_return from the op_return - opret = txin.vout[opret_ind].scriptPubKey; - } // else return(eval->Invalid("vin has wrong amount of vouts")); // dont think this is needed? - if ( DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid ) - { - fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s\n", i, txin.GetHash().ToString().c_str()); + fprintf(stderr, "vin.%i is not a payments CC vout: txid.%s vout.%i\n", i, txin.GetHash().ToString().c_str(), vin.prevout.n); return(eval->Invalid("vin is not paymentsCC type")); } } - // check the chain depth vs locked blocks requirement. - CBlockIndex* pblockindex = komodo_blockindex(blockhash); - if ( pblockindex == 0 || pblockindex->GetHeight() > ht-lockedblocks ) + else if ( IsPaymentsvout(cp,txin,vin.prevout.n,txidaddr,vinccopret) != 0 ) { - fprintf(stderr, "vin.%i is not elegible to be spent yet height.%i vs elegible_ht.%i\n", i, pblockindex!=0?pblockindex->GetHeight():0, ht-lockedblocks); + // if in txid address apply merge offset if applicable. + if ( fIsMerge && vinccopret.size() > 2 && DecodePaymentsMergeOpRet(vinccopret,checktxid) == 'M' ) + { + // Apply merge offset to locked blocks, this prevents people spaming payments fund and payments merge to prevent release happening. + mergeoffset = PAYMENTS_MERGEOFSET; + } + } + else // not from global payments plan, or txid address. + return(eval->Invalid("utxo comes from incorrect address")); + // check the chain depth vs locked blocks requirement. + if ( !payments_lockedblocks(blockhash, lockedblocks+mergeoffset, blocksleft) ) + { + fprintf(stderr, "vin.%i is not elegible for.%i blocks \n",i, blocksleft); return(eval->Invalid("vin not elegible")); } + i++; } else return(eval->Invalid("cant get vin transaction")); - i++; } - } else return(eval->Invalid("create transaction cannot decode")); - } else return(eval->Invalid("Could not get contract transaction")); + if ( fIsMerge ) + { + if ( i < 2 ) + return(eval->Invalid("must have at least 2 vins to carry out merge")); + else if ( i == dust+1 ) + return(eval->Invalid("cannot merge only dust")); + } + } else return(eval->Invalid("cannot decode create transaction")); + } else return(eval->Invalid("could not get contract transaction")); return(true); } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp - -int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t latestheight) +int64_t AddPaymentsInputs(bool fLockedBlocks,int8_t GetBalance,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey txidpk,int64_t total,int32_t maxinputs,uint256 createtxid,int32_t lockedblocks,int64_t minrelease,int32_t &blocksleft) { - char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx,tx; int32_t iter,vout,ht,n = 0; - std::vector > unspentOutputs; - if ( maxinputs > CC_MAXVINS ) - maxinputs = CC_MAXVINS; - if ( maxinputs > 0 ) - threshold = total/maxinputs; - else threshold = total; + char coinaddr[64]; CPubKey Paymentspk; int64_t nValue,threshold,price,totalinputs = 0; uint256 txid,checktxid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t iter,vout,ht,n = 0; + std::vector > unspentOutputs; CScript ccopret; + std::vector > blocksleft_balance; + if ( GetBalance == 0 ) + { + if ( maxinputs > CC_MAXVINS ) + maxinputs = CC_MAXVINS; + if ( maxinputs > 0 ) + threshold = total/maxinputs; + else threshold = total; + } + else threshold = 0; Paymentspk = GetUnspendable(cp,0); for (iter=0; iter<2; iter++) { + if ( GetBalance == 1 && iter == 1 ) + continue; // getbalance of global paymentsCC address. + if ( GetBalance == 2 && iter == 0 ) + continue; // get balance of txidpk address. if ( iter == 0 ) GetCCaddress(cp,coinaddr,Paymentspk); else GetCCaddress1of2(cp,coinaddr,Paymentspk,txidpk); @@ -352,54 +557,61 @@ int64_t AddPaymentsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CP txid = it->first.txhash; vout = (int32_t)it->first.index; //fprintf(stderr,"iter.%d %s/v%d %s\n",iter,txid.GetHex().c_str(),vout,coinaddr); - if ( vout == 0 && GetTransaction(txid,vintx,hashBlock,false) != 0 ) + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( latestheight != 0 ) + if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr,ccopret)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { - if ( (ht= komodo_blockheight(hashBlock)) == 0 ) + int32_t offset = 0; + if ( ccopret.size() > 2 ) { - fprintf(stderr,"null ht\n"); + if ( iter == 0 && (DecodePaymentsFundOpRet(ccopret,checktxid) != 'F' || checktxid != createtxid) ) + { + // global address but not for this plan. + fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str()); + continue; + } + // increase merge offset, if this is a merge tx, merging merged utxos. + if ( iter == 1 && GetBalance == 4 && DecodePaymentsMergeOpRet(ccopret,checktxid) == 'M' ) + offset = PAYMENTS_MERGEOFSET; + } + int32_t tmpblocksleft = 0; + if ( fLockedBlocks && !payments_lockedblocks(hashBlock, lockedblocks+offset, tmpblocksleft) ) + { + blocksleft_balance.push_back(std::make_pair(tmpblocksleft,nValue)); continue; } - else if ( ht > latestheight ) - { - fprintf(stderr,"ht.%d > lastheight.%d\n",ht,latestheight); - continue; - } - } - if ( iter == 0 ) - { - CScript opret; uint256 checktxid; int32_t opret_ind; - if ( (opret_ind= has_opret(vintx, EVAL_PAYMENTS)) == 0 ) - { - // get op_return from CCvout - opret = getCCopret(vintx.vout[0].scriptPubKey); - } - else - { - // get op_return from the op_return - opret = vintx.vout[opret_ind].scriptPubKey; - } - if ( myGetTransaction(txid,tx,hashBlock) == 0 || DecodePaymentsFundOpRet(opret,checktxid) != 'F' || checktxid != createtxid ) - { - fprintf(stderr,"bad opret %s vs %s\n",checktxid.GetHex().c_str(),createtxid.GetHex().c_str()); - continue; - } - } - if ( (nValue= IsPaymentsvout(cp,vintx,vout,coinaddr)) > PAYMENTS_TXFEE && nValue >= threshold && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) - { - if ( total != 0 && maxinputs != 0 ) + if ( (GetBalance == 0 && total != 0 && maxinputs != 0) || GetBalance == 4 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); nValue = it->second.satoshis; + if ( GetBalance == 4 && nValue < COIN ) + blocksleft++; // count dust with unused variable. + if ( GetBalance == 2 || GetBalance == 1 ) + blocksleft++; // count all utxos with unused variable. totalinputs += nValue; n++; //fprintf(stderr,"iter.%d %s/v%d %s %.8f\n",iter,txid.GetHex().c_str(),vout,coinaddr,(double)nValue/COIN); - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; + if ( GetBalance == 0 && ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) ) + break; // create tx. We have ebnough inputs to make it. } //else fprintf(stderr,"nValue %.8f vs threshold %.8f\n",(double)nValue/COIN,(double)threshold/COIN); } } } + if ( GetBalance == 3 && totalinputs < minrelease ) // return elegible balance to be spent, and blocks left until min release can be released. + { + int64_t lockedblocks_balance = totalinputs; // inputs that can be spent already. + // sort utxos by blocks until able to be spent, smallest at top. + std::sort(blocksleft_balance.begin(), blocksleft_balance.end()); + // iterate the utxos blocks left vector, to get block height min release is able to be released. + for ( auto utxo : blocksleft_balance ) + { + lockedblocks_balance += utxo.second; + if ( lockedblocks_balance >= minrelease ) + { + blocksleft = utxo.first; + break; + } + } + } return(totalinputs); } @@ -474,121 +686,244 @@ int32_t payments_parsehexdata(std::vector &hexdata,cJSON *item,int32_t UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) { - int32_t latestheight,nextheight = komodo_nextheight(); - CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock; - CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations,checkallocations=0,allocation; CTxOut vout; CScript onlyopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + LOCK(cs_main); + CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),komodo_nextheight()); UniValue result(UniValue::VOBJ); uint256 createtxid,hashBlock,tokenid; + CTransaction tx,txO; CPubKey mypk,txidpk,Paymentspk; int32_t i,n,m,numoprets=0,lockedblocks,minrelease; int64_t newamount,inputsum,amount,CCchange=0,totalallocations=0,checkallocations=0,allocation; CTxOut vout; CScript onlyopret,ccopret; char txidaddr[64],destaddr[64]; std::vector txidoprets; + int32_t top,bottom=0,blocksleft=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t funcid,fixedAmount=0,skipminimum=0; bool fFixedAmount = false; + mpz_t mpzTotalAllocations, mpzAllocation; mpz_init(mpzTotalAllocations); cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); - if ( params != 0 && n == 2 ) + if ( params != 0 && n >= 2 ) { createtxid = payments_juint256(jitem(params,0)); amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; - if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) + if ( n == 3 ) + skipminimum = juint(jitem(params,2),0); + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 && tx.vout.size() > 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( ((funcid= DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets)) == 'C' || (funcid= DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys)) == 'S' || (funcid= DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid)) == 'O') ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); return(result); } - latestheight = (nextheight - lockedblocks - 1); + // set minimum size to 10k sat otherwise the tx will be invalid. + if ( minimum < 10000 ) + minimum = 10000; if ( amount < minrelease*COIN ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","amount too smal")); result.push_back(Pair("amount",ValueFromAmount(amount))); result.push_back(Pair("minrelease",ValueFromAmount(minrelease*COIN))); + if ( params != 0 ) + free_json(params); return(result); } txidpk = CCtxidaddr(txidaddr,createtxid); - mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk)); - m = txidoprets.size(); - for (i=0; i> vData = std::vector>(); + if ( makeCCopret(ccopret, vData) ) + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,0,Paymentspk,txidpk,&vData)); + //fprintf(stderr, "funcid.%i\n", funcid); + if ( funcid == 'C' ) { - std::vector scriptPubKey,opret; - vout.nValue = 0; - if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + // normal payments + m = txidoprets.size(); + for (i=0; i 0 ) + std::vector scriptPubKey,opret; + vout.nValue = 0; + if ( myGetTransaction(txidoprets[i],txO,hashBlock) != 0 && txO.vout.size() > 1 && DecodePaymentsTxidOpRet(txO.vout[txO.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { - onlyopret.resize(opret.size()); - memcpy(&onlyopret[0],&opret[0],opret.size()); - numoprets++; - } - } else break; - mtx.vout.push_back(vout); + vout.nValue = allocation; + vout.scriptPubKey.resize(scriptPubKey.size()); + memcpy(&vout.scriptPubKey[0],&scriptPubKey[0],scriptPubKey.size()); + checkallocations += allocation; + if ( opret.size() > 0 ) + { + onlyopret.resize(opret.size()); + memcpy(&onlyopret[0],&opret[0],opret.size()); + numoprets++; + } + } else break; + mtx.vout.push_back(vout); + } + result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( i != m ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidoprets[i]")); + result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); + return(result); + } + else if ( checkallocations != totalallocations ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","totalallocations mismatch")); + result.push_back(Pair("checkallocations",(int64_t)checkallocations)); + result.push_back(Pair("totalallocations",(int64_t)totalallocations)); + if ( params != 0 ) + free_json(params); + return(result); + } + else if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many oprets")); + if ( params != 0 ) + free_json(params); + return(result); + } + // set totalallocations to a mpz_t bignum, for amounts calculation later. + mpz_set_si(mpzTotalAllocations,totalallocations); } - result.push_back(Pair("numoprets",(int64_t)numoprets)); - if ( i != m ) + else if ( funcid == 'S' || funcid == 'O' ) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","invalid txidoprets[i]")); - result.push_back(Pair("txi",(int64_t)i)); - if ( params != 0 ) - free_json(params); - return(result); - } - else if ( checkallocations != totalallocations ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","totalallocations mismatch")); - result.push_back(Pair("checkallocations",(int64_t)checkallocations)); - result.push_back(Pair("totalallocations",(int64_t)totalallocations)); - if ( params != 0 ) - free_json(params); - return(result); - } - else if ( numoprets > 1 ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","too many oprets")); - if ( params != 0 ) - free_json(params); - return(result); + // normal snapshot + if ( vAddressSnapshot.size() == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","first snapshot has not happened yet")); + if ( params != 0 ) + free_json(params); + return(result); + } + if ( top > 3999 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot pay more than 3999 addresses")); + if ( params != 0 ) + free_json(params); + return(result); + } + if ( fixedAmount == 7 ) + { + // game setting, randomise bottom and top values + fFixedAmount = payments_game(top,bottom); + } + else if ( fixedAmount != 0 ) + { + fFixedAmount = true; + } + if ( (top-bottom) < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid range top/bottom")); + if ( params != 0 ) + free_json(params); + return(result); + } + + std::vector allocations; + std::vector scriptPubKeys; + if ( funcid == 'S' ) + m = payments_getallocations(top, bottom, excludeScriptPubKeys, mpzTotalAllocations, scriptPubKeys, allocations); + else + { + // token snapshot + // payments_gettokenallocations(top, bottom, excludeScriptPubKeys, tokenid, mpzTotalAllocations, scriptPubKeys, allocations); + } + if ( (allocations.size() == 0 || scriptPubKeys.size() == 0 || allocations.size() != scriptPubKeys.size()) ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","mismatched allocations, scriptpubkeys")); + if ( params != 0 ) + free_json(params); + return(result); + } + i = 0; + for ( auto allocation : allocations ) + { + vout.nValue = allocation; + vout.scriptPubKey = scriptPubKeys[i]; + mtx.vout.push_back(vout); + i++; + } } newamount = amount; + int64_t totalamountsent = 0; + mpz_t mpzAmount; mpz_init(mpzAmount); mpz_set_si(mpzAmount,amount); for (i=0; i= newamount+2*PAYMENTS_TXFEE ) - { - std::string rawtx; - if ( (CCchange= (inputsum - newamount - 2*PAYMENTS_TXFEE)) >= PAYMENTS_TXFEE ) - mtx.vout[0].nValue = CCchange; - mtx.vout.push_back(CTxOut(PAYMENTS_TXFEE,CScript() << ParseHex(HexStr(txidpk)) << OP_CHECKSIG)); - GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); - CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); - if ( params != 0 ) - free_json(params); - result.push_back(Pair("amount",ValueFromAmount(amount))); - result.push_back(Pair("newamount",ValueFromAmount(newamount))); - return(payments_rawtxresult(result,rawtx,1)); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt find enough locked funds")); - } + if ( totalamountsent < amount ) newamount = totalamountsent; + //int64_t temptst = mpz_get_si(mpzTotalAllocations); + //fprintf(stderr, "checkamount RPC.%li totalallocations.%li\n",totalamountsent, temptst); + mpz_clear(mpzAmount); mpz_clear(mpzTotalAllocations); } else { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt decode paymentscreate txid opret")); + if ( params != 0 ) + free_json(params); + return(result); + } + if ( (inputsum= AddPaymentsInputs(true,0,cp,mtx,txidpk,newamount+2*PAYMENTS_TXFEE,CC_MAXVINS/2,createtxid,lockedblocks,minrelease,blocksleft)) >= newamount+2*PAYMENTS_TXFEE ) + { + std::string rawtx; + mtx.vout[0].nValue = inputsum - newamount - PAYMENTS_TXFEE; // only 1 txfee, so the minimum in this vout is a tx fee. + GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,onlyopret); + if ( params != 0 ) + free_json(params); + result.push_back(Pair("amount",ValueFromAmount(amount))); + result.push_back(Pair("newamount",ValueFromAmount(newamount))); + return(payments_rawtxresult(result,rawtx,1)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough locked funds")); } } else @@ -610,24 +945,28 @@ UniValue PaymentsRelease(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); - CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount,totalallocations; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,lockedblocks,minrelease; std::vector txidoprets; + CPubKey Paymentspk,mypk,txidpk; uint256 txid,hashBlock; int64_t amount,totalallocations; CScript opret; CTransaction tx; char txidaddr[64]; std::string rawtx; int32_t n,useopret = 0,broadcast=0,lockedblocks,minrelease; std::vector txidoprets; + int32_t top,bottom,minimum=10000; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); - if ( params != 0 && n > 1 && n <= 3 ) + if ( params != 0 && n > 1 && n <= 4 ) { txid = payments_juint256(jitem(params,0)); amount = jdouble(jitem(params,1),0) * SATOSHIDEN + 0.0000000049; if ( n == 3 ) useopret = jint(jitem(params,2),0) != 0; - if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 ) + if ( n == 4 ) + broadcast = jint(jitem(params,3),0) != 0; + if ( myGetTransaction(txid,tx,hashBlock) == 0 || tx.vout.size() == 1 || (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 && DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 0 && DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid) == 0) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","invalid createtxid")); } else if ( AddNormalinputs(mtx,mypk,amount+PAYMENTS_TXFEE,60) > 0 ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -643,17 +982,13 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) else { opret = EncodePaymentsFundOpRet(txid); - fprintf(stderr, "opret.%s\n", HexStr(opret.begin(), opret.end()).c_str()); std::vector> vData = std::vector>(); if ( makeCCopret(opret, vData) ) - { mtx.vout.push_back(MakeCC1vout(EVAL_PAYMENTS,amount,Paymentspk,&vData)); - fprintf(stderr, "params_size.%li parmas_hexstr.%s\n", vData.size(), HexStr(vData[0].begin(),vData[0].end()).c_str()); - } } - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); if ( params != 0 ) - free_json(params); + free_json(params); return(payments_rawtxresult(result,rawtx,1)); } else @@ -672,18 +1007,88 @@ UniValue PaymentsFund(struct CCcontract_info *cp,char *jsonstr) return(result); } +UniValue PaymentsMerge(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); + CPubKey Paymentspk,mypk,txidpk; uint256 createtxid,hashBlock; int64_t inputsum,totalallocations=0; CScript opret; CTransaction tx; char txidaddr[64],destaddr[64]; std::string rawtx; + int32_t n,lockedblocks,minrelease,top,bottom,minimum=10000,blocksleft; std::vector txidoprets; + std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; + cJSON *params = payments_reparse(&n,jsonstr); + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( params != 0 && n == 1 ) + { + createtxid = payments_juint256(jitem(params,0)); + txidpk = CCtxidaddr(txidaddr,createtxid); + if ( myGetTransaction(createtxid,tx,hashBlock) == 0 || tx.vout.size() == 1 || (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 0 && DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 0 && DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid) == 0) ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid createtxid")); + } + else if ( (inputsum= AddPaymentsInputs(true,4,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft)) > 0 && mtx.vin.size() > 1 ) + { + int32_t dust = blocksleft; + if ( mtx.vin.size() == dust+1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot merge only dust")); + } + else + { + // encode the checktxid into the end of the ccvout, along with 'M' to flag merge type tx. + opret = EncodePaymentsMergeOpRet(createtxid); + std::vector> vData = std::vector>(); + if ( makeCCopret(opret, vData) ) + mtx.vout.push_back(MakeCC1of2vout(EVAL_PAYMENTS,inputsum-PAYMENTS_TXFEE,Paymentspk,txidpk,&vData)); + GetCCaddress1of2(cp,destaddr,Paymentspk,txidpk); + CCaddr1of2set(cp,Paymentspk,txidpk,cp->CCpriv,destaddr); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,CScript()); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find enough funds")); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + UniValue PaymentsTxidopret(struct CCcontract_info *cp,char *jsonstr) { CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); UniValue result(UniValue::VOBJ); CPubKey mypk; std::string rawtx; - std::vector scriptPubKey,opret; int32_t n,retval0,retval1=0; int64_t allocation; + std::vector scriptPubKey,opret; int32_t n,retval0,retval1=0; int64_t allocation; CScript test; txnouttype whichType; cJSON *params = payments_reparse(&n,jsonstr); mypk = pubkey2pk(Mypubkey()); if ( params != 0 && n > 1 && n <= 3 ) { allocation = (int64_t)jint(jitem(params,0),0); - retval0 = payments_parsehexdata(scriptPubKey,jitem(params,1),0); - CScript test = CScript(scriptPubKey.begin(),scriptPubKey.end()); - txnouttype whichType; + std::string address; + address.append(jstri(params,1)); + CTxDestination destination = DecodeDestination(address); + if ( IsValidDestination(destination) ) + { + // its an address + test = GetScriptForDestination(destination); + scriptPubKey = std::vector (test.begin(),test.end()); + } + else + { + // its a scriptpubkey + retval0 = payments_parsehexdata(scriptPubKey,jitem(params,1),0); + test = CScript(scriptPubKey.begin(),scriptPubKey.end()); + } if (!::IsStandard(test, whichType)) { result.push_back(Pair("result","error")); @@ -738,6 +1143,7 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) for (i=0; i scriptPubKey,opret; int64_t allocation; + //fprintf(stderr, "txid.%s\n",txidoprets[i].GetHex().c_str()); if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) { totalallocations += allocation; @@ -789,63 +1195,83 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) { - // need to code: exclude list of tokenid, dust threshold, maxpayees, excluded pubkeys[] CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease; std::string rawtx; int64_t totalallocations = 0; + UniValue result(UniValue::VOBJ); + uint256 hashBlock; CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::string rawtx; + int32_t lockedblocks,minrelease,top,bottom,n,i,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount; + if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cannot use airdrop wihtout -ac_snapshot set.")); + return(result); + } cJSON *params = payments_reparse(&n,jsonstr); - if ( params != 0 && n >= 4 ) + if ( params != 0 && n >= 5 ) { lockedblocks = juint(jitem(params,0),0); minrelease = juint(jitem(params,1),0); - if ( lockedblocks < 0 || minrelease < 0 ) + minimum = juint(jitem(params,2),0); + if ( minimum < 10000 ) minimum = 10000; + top = juint(jitem(params,3),0); + bottom = juint(jitem(params,4),0); + fixedAmount = juint(jitem(params,5),0); // fixed amount is a flag, set to 7 does game mode, 0 normal snapshot, anything else fixed allocations. + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || minimum < 0 || fixedAmount < 0 || top > 3999 ) { result.push_back(Pair("result","error")); - result.push_back(Pair("error","negative parameter")); + result.push_back(Pair("error","negative parameter, or top over 3999")); if ( params != 0 ) free_json(params); return(result); } - for (i=0; i scriptPubKey,opret; int64_t allocation; - if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) - { - totalallocations += allocation; - if ( opret.size() > 0 ) - numoprets++; - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","invalid txidopret")); - result.push_back(Pair("txid",txidoprets[i].GetHex())); - result.push_back(Pair("txi",(int64_t)i)); - if ( params != 0 ) - free_json(params); - return(result); - } - } - if ( numoprets > 1 ) + if ( top-bottom < 0 ) { result.push_back(Pair("result","error")); - result.push_back(Pair("error","too many opreturns")); - result.push_back(Pair("numoprets",(int64_t)numoprets)); + result.push_back(Pair("error","invalid range, top/bottom")); if ( params != 0 ) free_json(params); return(result); } + if ( n > 6 ) + { + for (i=0; i vscriptPubKey; + CScript scriptPubKey = GetScriptForDestination(destination); + vscriptPubKey.assign(scriptPubKey.begin(), scriptPubKey.end()); + excludeScriptPubKeys.push_back(vscriptPubKey); + } + } mypk = pubkey2pk(Mypubkey()); Paymentspk = GetUnspendable(cp,0); if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) { mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); + CScript tempopret = EncodePaymentsSnapsShotOpRet(lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys); + if ( tempopret.size() > 10000 ) // TODO: Check this! + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","op_return is too big, try with less exclude addresses.")); + if ( params != 0 ) + free_json(params); + return(result); + } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,tempopret); if ( params != 0 ) free_json(params); return(payments_rawtxresult(result,rawtx,1)); - } + } result.push_back(Pair("result","error")); result.push_back(Pair("error","not enough normal funds")); } @@ -859,17 +1285,102 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) return(result); } +UniValue PaymentsAirdropTokens(struct CCcontract_info *cp,char *jsonstr) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); + uint256 hashBlock, tokenid = zeroid; CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::string rawtx; + int32_t lockedblocks,minrelease,top,bottom,n,i,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount; + cJSON *params = payments_reparse(&n,jsonstr); + // disable for now. Need token snapshot function. + if ( 0 ) //params != 0 && n >= 6 ) + { + tokenid = payments_juint256(jitem(params,0)); + lockedblocks = juint(jitem(params,1),0); + minrelease = juint(jitem(params,2),0); + minimum = juint(jitem(params,3),0); + if ( minimum < 10000 ) minimum = 10000; + top = juint(jitem(params,4),0); + bottom = juint(jitem(params,5),0); + fixedAmount = juint(jitem(params,6),0); // fixed amount is a flag, set to 7 does game mode, 0 normal snapshot, anything else fixed allocations. + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || minimum < 0 || fixedAmount < 0 || top > 3999 || tokenid == zeroid ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter, or top over 3999")); + if ( params != 0 ) + free_json(params); + return(result); + } + // TODO: lookup tokenid and make sure it exists. + if ( n > 7 ) + { + for (i=0; i vscriptPubKey; + CScript scriptPubKey = GetScriptForDestination(destination); + vscriptPubKey.assign(scriptPubKey.begin(), scriptPubKey.end()); + excludeScriptPubKeys.push_back(vscriptPubKey); + } + } + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); + CScript tempopret = EncodePaymentsTokensOpRet(lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid); + if ( tempopret.size() > 10000 ) // TODO: Check this! + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","op_return is too big, try with less exclude pubkeys.")); + if ( params != 0 ) + free_json(params); + return(result); + } + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,tempopret); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,0)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough normal funds")); + } + else + { + result.push_back(Pair("result","error")); + //result.push_back(Pair("error","parameters error")); + result.push_back(Pair("error","tokens airdrop not yet impmlemented")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease; std::vector txidoprets; int64_t funds,fundsopret,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease,blocksleft=0; std::vector txidoprets; int64_t funds,fundsopret,elegiblefunds,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; + int32_t top,bottom,minimum=10000; std::vector> excludeScriptPubKeys; // snapshot + uint256 tokenid; int8_t fixedAmount; CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),komodo_nextheight()); cJSON *params = payments_reparse(&n,jsonstr); if ( params != 0 && n == 1 ) { Paymentspk = GetUnspendable(cp,0); createtxid = payments_juint256(jitem(params,0)); - if ( myGetTransaction(createtxid,tx,hashBlock) != 0 ) + if ( myGetTransaction(createtxid,tx,hashBlock) != 0 && tx.vout.size() > 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) + if ( DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) != 0 ) { if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) { @@ -879,6 +1390,7 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) free_json(params); return(result); } + result.push_back(Pair("plan_type","payments")); result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); result.push_back(Pair("totalallocations",(int64_t)totalallocations)); result.push_back(Pair("minrelease",(int64_t)minrelease)); @@ -907,29 +1419,89 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) } else fprintf(stderr,"error decoding voutsize.%d\n",(int32_t)txO.vout.size()); a.push_back(obj); } - flag++; result.push_back(Pair("numoprets",(int64_t)numoprets)); if ( numoprets > 1 ) { + flag++; result.push_back(Pair("result","error")); result.push_back(Pair("error","too many opreturns")); - } - else + } else result.push_back(Pair("txidoprets",a)); + } + else if ( DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 || bottom < 0 || fixedAmount < 0 || top > 3999 || minimum < 10000 ) { - result.push_back(Pair("txidoprets",a)); - txidpk = CCtxidaddr(txidaddr,createtxid); - GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); - funds = CCaddress_balance(fundsaddr,1); - result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); - GetCCaddress(cp,fundsopretaddr,Paymentspk); - fundsopret = CCaddress_balance(fundsopretaddr,1); // Shows balance for ALL payments plans, not just the one asked for! - result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); - result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); - result.push_back(Pair("result","success")); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); } + if ( fixedAmount == 7 && payments_game(top,bottom)) + result.push_back(Pair("plan_type","payments_game")); + else + result.push_back(Pair("plan_type","snapshot")); + result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); + result.push_back(Pair("minrelease",(int64_t)minrelease)); + result.push_back(Pair("minimum",(int64_t)minimum)); + result.push_back(Pair("bottom",(int64_t)bottom)); + result.push_back(Pair("top",(int64_t)top)); + result.push_back(Pair("fixedFlag",(int64_t)fixedAmount)); + for ( auto scriptPubKey : excludeScriptPubKeys ) + { + CTxDestination dest; + if ( ExtractDestination(CScript(scriptPubKey.begin(),scriptPubKey.end()), dest) ) + a.push_back(CBitcoinAddress(dest).ToString()); + } + result.push_back(Pair("excludeAddresses",a)); + } + else if ( DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid) != 0 ) + { + if ( lockedblocks < 0 || minrelease < 0 || top <= 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + result.push_back(Pair("plan_type","token snapshot")); + result.push_back(Pair("lockedblocks",(int64_t)lockedblocks)); + result.push_back(Pair("minrelease",(int64_t)minrelease)); + result.push_back(Pair("top",(int64_t)top)); + result.push_back(Pair("tokenid",tokenid.ToString())); + // TODO: show pubkeys instead of scriptpubkeys + for ( auto scriptPubKey : excludeScriptPubKeys ) + a.push_back(HexStr(scriptPubKey.begin(),scriptPubKey.end())); + result.push_back(Pair("excludeScriptPubkeys",a)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt decode valid payments create txid opreturn")); + } + if ( flag == 0 ) + { + txidpk = CCtxidaddr(txidaddr,createtxid); + GetCCaddress1of2(cp,fundsaddr,Paymentspk,txidpk); + blocksleft = 0; + funds = AddPaymentsInputs(false,2,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft); + result.push_back(Pair(fundsaddr,ValueFromAmount(funds))); + result.push_back(Pair("utxos", (int64_t)blocksleft)); + blocksleft = 0; + GetCCaddress(cp,fundsopretaddr,Paymentspk); + fundsopret = AddPaymentsInputs(false,1,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft); + result.push_back(Pair(fundsopretaddr,ValueFromAmount(fundsopret))); + result.push_back(Pair("utxos", (int64_t)blocksleft)); + result.push_back(Pair("totalfunds",ValueFromAmount(funds+fundsopret))); + // Blocks until minrelease can be released. + elegiblefunds = AddPaymentsInputs(true,3,cp,mtx,txidpk,0,CC_MAXVINS,createtxid,lockedblocks,minrelease,blocksleft); + result.push_back(Pair("elegiblefunds",ValueFromAmount(elegiblefunds))); + result.push_back(Pair("min_release_height",chainActive.Height()+blocksleft)); + result.push_back(Pair("result","success")); } } - if ( flag == 0 ) + else { result.push_back(Pair("result","error")); result.push_back(Pair("error","couldnt find valid payments create txid")); @@ -947,8 +1519,9 @@ UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) { - std::vector > addressIndex; uint256 txid,hashBlock; - UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease; std::vector txidoprets; int64_t totalallocations; + std::vector > addressIndex; uint256 txid,hashBlock,tokenid; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); char markeraddr[64],str[65]; CPubKey Paymentspk; CTransaction tx; int32_t lockedblocks,minrelease; std::vector txidoprets; int64_t totalallocations=0; + int32_t top=0,bottom=0,minimum=10000; std::vector> excludeScriptPubKeys; int8_t fixedAmount = 0; Paymentspk = GetUnspendable(cp,0); GetCCaddress1of2(cp,markeraddr,Paymentspk,Paymentspk); SetCCtxids(addressIndex,markeraddr,true); @@ -957,9 +1530,9 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) txid = it->first.txhash; if ( it->first.index == 0 && myGetTransaction(txid,tx,hashBlock) != 0 ) { - if ( tx.vout.size() > 0 && DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' ) + if ( tx.vout.size() > 0 && (DecodePaymentsOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,totalallocations,txidoprets) == 'C' || DecodePaymentsSnapsShotOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys) == 'S' || DecodePaymentsTokensOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,lockedblocks,minrelease,minimum,top,bottom,fixedAmount,excludeScriptPubKeys,tokenid) == 'O') ) { - if ( lockedblocks < 0 || minrelease < 0 || totalallocations <= 0 || txidoprets.size() < 2 ) + if ( lockedblocks < 0 || minrelease < 0 || (totalallocations <= 0 && top <= 0 ) || bottom < 0 || fixedAmount < 0 || minimum < 10000 ) { result.push_back(Pair("result","error")); result.push_back(Pair("error","negative parameter")); @@ -970,6 +1543,6 @@ UniValue PaymentsList(struct CCcontract_info *cp,char *jsonstr) } } result.push_back(Pair("result","success")); - result.push_back(Pair("createtxids",a)); - return(result); + result.push_back(Pair("createtxids",a)); + return(result); } diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 407cd81fa..2c0a33b20 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -11,138 +11,139 @@ * * * Removal or modification of this copyright notice is prohibited. * * * - ******************************************************************************/ + *****************************************************************************/ -#include "CCassets.h" -#include "CCPrices.h" + /* + CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; -/* -CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; - 0.5% fee based on betamount, NOT leveraged betamount!! 0.1% collected by price basis determinant 0.2% collected by rekt tx - At the simplest, prices CC allows to make leveraged cash settled bets on long and short BTCUSD: - BTCUSD, 1 with positive leverage parameter up to 777 - BTCUSD, 1 with negative leverage parameter up to -777 - - These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define - - Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection address (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) - - The definition of the synthetic instrument that is being tracked is actually defined with a forth type of syntax and is quite flexible to allow unlimited combinations and weights, but that is an independent aspect and will be covered later. - - Let us discuss the simplest synthetic case of a long or short of BTCUSD (or any other direct pricepoint from the trustless oracle). If you look at the charts, you will see the blue line that is direct from the miners (and therefore cant be directly used). The orange line is the 51% correlated price that is deterministically random selected from the prior 24 hours. And finally the green line which is simply the average value from the priot 24 hours. - - We will assume that the orange and green prices are able to be deterministically calculated from the raw coinbase data (blue). The prices rpc is currently working reasonably well and appears to return deterministic prices, however for use in the prices CC, it is often needed to find just a single data point. To that effect there is the temporary function prices_syntheticprice, which uses a bruteforce and totally inefficient way to calculate the correlated and smoothed prices. This inefficient process will need to be changed to a permanent storage of correlated and smoothed prices for each trustless oracle that is updated each block. This will then allow to directly lookup each value for any of the trustless prices. Such speed will indeed be needed to scale up usage of a REKT chain. - - Since the mined prices can be manipulated by any miner, combined with the +/-1% tolerance, it would be possible for an external miner to obtain a large hashrate and skew the price +1%, then to -1% to create a 2% price difference, which under high leverage might be able to generate a large profit. - - To avoid this precisely controllable biasing, the 51% correlation of past day is used (orange), which makes the price jump around a lot. The green line that sums the prior day is needed to remove this jitter back to the average value. However, this creates a 1.5 day delay to the price movement of the smoothed price compared to the current price. What this means is that if we want to use the green line to settle prices automatically, a trivial way to make money is to bet in the direct that the mined prices are relative to the smoothed prices. Given 1.5 day head start, it wont be any mystery which direction the smoothed line will move in the next 24 hours. - - So this makes the smoothed price unusable for settling the bets with. However, we cant use the correlated price either, as it would also allow to make bets when the correlated price picked a significantly lower/higher price than the raw price. The solution is to have a floating basis for the costbasis of the bet. In order to allow finding the right costbasis, for long bets the maximum price between the correlated and smoothed price is needed over the first day after the bet. For short bets, the minimum price is needed. - - What this means is that the actual starting price for a bet wont be known for sure when the bet is made, but if the price is relatively stable, there wont be much of a change. If there is a lot of recent volatility, then the starting price will have a high variability. In order to calculate the costbasis, it currently requires many calculations to find the MAX (or MIN) for the first day. Even when this is made to be a lookup, it still requires to issue a transaction to change the state of a bet from a "starting" state to a "in effect" state and this state change is indicated by whether the bettx vout that contains the costbasis utxo is spent or not. - - Once a bet goes into effect, then block by block, it is checked by the decentralized set of rekt scanners if it is rekt or not, in this case for double the reward for calculating the cost basis. This fully decentralized mechanism ensures that some node will decide to collect the free money and ensures that the bankroll is growing. To miss a rekt bet and not close it out when it can be is to allow it to survive for another block, which can change it profitability from negative to positive. - - Which comes to how profits are calculated. Once the costbasis is locked, then the profit calculation can be made based on the ratio between the costbasis and the current price, multiplied by the leverage. So, if a long position gains 10% at a 10x leverage, then approximately the entire bet amount will be made, ie. a double. Similarily with a short position, a 10% drop in price at 10x leverage will double the bet amount. Since it takes a full day to establish the costbasis, it is not possible to allow changing the costbasis for an existing bet, however it is possible to add funds so that it is less likely to be rekt. The sum of the initial bet and all added funds must be greater than the loss at every block to prevent a position from being rekt. To make it simple to calculate the amount of funds added, another bettx vout is used as a baton. Techniques similar to rogue CC to find where the bettx vout was spent and looking at each such transaction to find the total funds added can be used. - - The once that makes the bet is able to add funds or cashout at any block, as long as it isnt rekt. If it is rekt, the bettor is able to collect the rekt fee, so at least that is some consolation, but it is advised to increase the total funding to avoid being rekt. On a cashout all the funds that were bet adjusted by the profits can be collected. - - Hopefully the above description makes it clear what the following rpc calls should be doing: - - UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) - funds are locked into 1of2 global CC address - for first day, long basis is MAX(correlated,smoothed), short is MIN() - reference price is the smoothed of the previous block - if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price - original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund - 0.5% of bet -> globalfund + PricesBet -> +/-leverage, amount, synthetic -> opreturn includes current price + funds are locked into 1of2 global CC address + for first day, long basis is MAX(correlated,smoothed), short is MIN() + reference price is the smoothed of the previous block + if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price + original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund + 0.5% of bet -> globalfund - UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) - add funding to an existing bet, doesnt change the profit calcs but does make the bet less likely to be rekt + PricesStatus -> bettxid maxsamples returns initial params, cost basis, amount left, rekt:true/false, rektheight, initial synthetic price, current synthetic price, net gain + + PricesRekt -> bettxid height -> 0.1% to miner, rest to global CC + + PricesClose -> bettxid returns (synthetic value + amount) + + PricesList -> all bettxid -> list [bettxid, netgain] - UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) - in the first day from the bet, the costbasis can (and usually does) change based on the MAX(correlated,smoothed) for long and MIN() for shorts - to calculate this requires a bit of work, so whatever node is first to get the proper calculation confirmed, gets a 0.1% costbasis fee - - UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) - similarily, any node can submit a rekt tx and cash in on 0.2% fee if their claim is confirmed - - UniValue PricesCashout(uint64_t txfee,uint256 bettxid) - only the actually creator of bet is able to cashout and only if it isnt rekt at that moment - - UniValue PricesInfo(uint256 bettxid,int32_t refheight) - all relevant info about a bet - - UniValue PricesList() - a list of all pending and completed bets in different lists - - - Appendix Synthetic position definition language: - - Let us start from the familiar BTCUSD nomenclature. this is similar (identical) to forex EURUSD and the equivalent. Notice the price needs two things as it is actually a ratio, ie. BTCUSD is how many USD does 1 BTC get converted to. It can be thought of as the value of BTC/USD, in other words a ratio. - - The value of BTC alone, or USD alone, it is actually quite an abstract issue and it can only be approximated. Specific ways of how to do this can be discussed later, for now it is only important to understand that all prices are actually ratios. - - And these ratios work as normal algebra does. So a/b * b/c == a/c! You can try this out with BTCUSD and USDJPY -> BTC/USD * USD/JPY -> BTC/JPY or the BTCJPY price - - division is the reciprocal, so BTCUSD reciprocated is USDBTC - - Without getting into all the possible combinations of things, let us first understand what the language allows. It uses a stack based language, where individual tokens update the state. The final state needs to have an empty stack and it will have a calculated price. - - The most important is pushing a specific price onto the stack. All the correlated and smoothed prices are normalized so it has each integer unit equal to 0.00000001, amounts above 100 million are above one, amounts less are below one. 100 million is the unit constant. - - In the prices CC synthetic definition language, the symbol that is returned pushes the adjusted price to the stack. The adjustment is selecting between the correlated and smoothed if within a day from the bet creation, or just the smoothed if after a day. You can have a maximum depth of 3, any more than that and it should return an error. - - This means there are operations that are possible on 1, 2 and 3 symbols. For 1 symbol, it is easy, the only direct operation is the inverse, which is represented by "!". All items in the language are separated by "," - - "BTCUSD, !" - - The above is the inverse or USD/BTC, which is another way to short BTCUSD. It is also possible to short the entire synthetic with a negative leverage. The exact results of a -1 leverage and inverse, might not be exact due to the math involved with calculating the profit, but it should generally be similar. - - For two symbols, there is a bit more we can do, namely multiply and divide, combined with inverting, however to simplify the language any inverting is required to be done by the ordering of the symbols and usage of multiply or divide. multiply is "*" and divide is "/" the top of the stack (last to be pushed) is the divisor, the one right before the divisor is the numerator. - - "BTCUSD, USDJPY, *" <- That will create a BTCJPY synthetic - - "BTCEUR, BTCUSD, /" <- That will create a USDEUR synthetic - - If you experiment around with this, you will see that given two symbols and the proper order and * or /, you can always create the synthetic that you want, assuming what you want is the cancelling out of the term in common with the two symbols and the resulting ratio is between the two unique terms. */ -// Now we get to the three symbol operations, which there are 4 of *//, **/, *** and /// - /* - these four operators work on the top of the stack in left to right order as the syntax of the definition lists it, though it is even possible to have the value from an earlier computation on the top of the stack. Ultimately all three will be multiplied together, so a * in a spot means it us used without changing. A / means its inverse will be used. - - "KMDBTC, BTCUSD, USDJPY, ***" <- this would create a KMDJPY synthetic. The various location of the / to make an inverse is to orient the raw symbol into the right orientation as the pricefeed is only getting one orientation of the ratio. +To create payments plan start a chain with the following ac_params: + -ac_snapshot=1440 (or for test chain something smaller, if you like.) + - this enables the payments airdrop cc to work. + -ac_earlytxidcontract=237 (Eval code for prices cc.) + - this allows to know what contract this chain is paying with the scriptpubkey in the earlytxid op_return. - So now we have covered all ways to map 1, 2 and 3 values on the top of the stack. A value can be on the top of the stack directly from a symbol, or as the result of some 1, 2 or 3 symbol operation. With a maximum stack depth of 3, it might require some experimentation to get a very complex synthetic to compile. Alternately, a stack deeper than 3 might be the acceptable solution if there are a family of synthetics that need it. - - At this point, it is time to describe the weights. It turns out that all above examples are incomplete and the synthetic descriptions are all insufficient and should generate an error. The reason is that the actual synthetic price is the value of the accumulator, which adds up all the weighted prices. In order to assign a weight to a price value on the stack, you simply use a number that is less than 2048. - - What such a weight number does, is consume the top of the stack, which also must be at depth of 1 and adds it to the accumulator. This allows combining multiple different synthetics into a meta synthetic, like for an aggregated index that is weighted by marketcap, or whatever other type of ratio trade that is desired. - - "BTCUSD, 1000, ETHBTC, BTCUSD, *, 300" -> that creates a dual index of BTCUSD and ETHUSD with a 30% ETH weight - - all weight operations consumes the one and only stack element and adds its weight to the accumulator, using this a very large number of terms can be all added together. Using inverses, allows to get the short direction into the equation mixed with longs, but all terms must be positive. If you want to create a spread between two synthetics, you need to create two different synthetics and go long with one and short with another. - - "BTCUSD, 1" with leverage -2 and "KMDBTC, BTCUSD, *, 1" with leverage 1 this will setup a +KMDUSD -2 BTCUSD spread when the two are combined, and would be at breakeven when KMDUSD gains 2x more percentage wise than BTC does. anytime KMD gains more, will be positive, if the gains are less, would be negative. - - Believe it or not, the string to binary compiler for synthetic description and interpretation of it is less than 200 lines of code. - - todo: complete all the above, bet tokens, cross chain prices within cluster These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define - - Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection scriptPubKey (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) this requires adding new vouts - - Modification: in the event there is one price in the accumulator and one price on the stack at the end, then it is a (A - B) spread - - Monetizations should be sent to: RGsWqwFviaNJSbmgWi6338NL2tKkY7ZqKL - +./komodod -ac_name=TESTPRC -ac_supply=100000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -ac_snapshot=50 -ac_sapling=1 -ac_earlytxidcontract=237 -testnode=1 -gen -genproclimit=1 + +Then in very early block < 10 or so, do paymentsairdrop eg. + `./komodo-cli -ac_name=TESTPRC paymentsairdrop '[10,10,0,3999,0,0]' +Once this tx is confirmed, do `paymentsfund` and decode the raw hex. You can edit the source to not send the tx if requried. +Get the full `hex` of the vout[0] that pays to CryptoCondition. then place it on chain with the following command: with the hex you got in place of the hex below. + './komodo-cli -ac_name=TESTPRC opreturn_burn 1 2ea22c8020292ba5c8fd9cc89b12b35bf8f5d00196990ecbb06102b84d9748d11d883ef01e81031210008203000401cc' +copy the hex, and sendrawtransaction, copy the txid returned. +this places the scriptpubkey that pays the plan into an op_return before block 100, allowing us to retreive it, and nobody to change it. +Restart the daemon with -earlytxid= eg: + +./komodod -ac_name=TESTPRC -ac_supply=100000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -ac_snapshot=50 -ac_sapling=1 -ac_earlytxidcontract=237 -earlytxid=cf89d17fb11037f65c160d0749dddd74dc44d9893b0bb67fe1f96c1f59786496 -testnode=1 -gen -genproclimit=1 + +mine the chain past block 100, preventing anyone else, creating another payments plan on chain before block 100. + +We call the following in Validation and RPC where the address is needed. +if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + GetKomodoEarlytxidScriptPub(); + +This will fetch the op_return, calculate the scriptPubKey and save it to the global. +On daemon restart as soon as validation for BETTX happens the global will be filled, after this the transaction never needs to be looked up again. +GetKomodoEarlytxidScriptPub is on line #2080 of komodo_bitcoind.h */ +#include "CCassets.h" +#include "CCPrices.h" + +#include +#include + +#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) + +#define NVOUT_CCMARKER 1 +#define NVOUT_NORMALMARKER 3 + +typedef struct OneBetData { + int64_t positionsize; + int32_t firstheight; + int64_t costbasis; + int64_t profits; + + OneBetData() { positionsize = 0; firstheight = 0; costbasis = 0; profits = 0; } // it is important to clear costbasis as it will be calculated as minmax from inital value 0 +} onebetdata; + +typedef struct BetInfo { + uint256 txid; + int64_t averageCostbasis, firstprice, lastprice, liquidationprice, equity; + int64_t exitfee; + int32_t lastheight; + int16_t leverage; + bool isOpen, isRekt; + uint256 tokenid; + + std::vector vecparsed; + std::vector bets; + CPubKey pk; + + bool isUp; + + BetInfo() { + averageCostbasis = firstprice = lastprice = liquidationprice = equity = 0; + lastheight = 0; + leverage = 0; + exitfee = 0; + isOpen = isRekt = isUp = false; + } +} BetInfo; + +typedef struct MatchedBookTotal { + + int64_t diffLeveragedPosition; + +} MatchedBookTotal; + +typedef struct TotalFund { + int64_t totalFund; + int64_t totalActiveBets; + int64_t totalCashout; + int64_t totalRekt; + int64_t totalEquity; + + TotalFund() { + totalFund = totalActiveBets = totalCashout = totalRekt = totalEquity = 0; + } + +} TotalFund; + +int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t height, int16_t leverage, std::vector vec, int64_t positionsize, int64_t &profits, int64_t &outprice); +static bool prices_isacceptableamount(const std::vector &vecparsed, int64_t amount, int16_t leverage); + +// helpers: + +// returns true if there are only digits and no alphas or slashes in 's' +inline bool is_weight_str(std::string s) { + return + std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); } ) > 0 && + std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isalpha(c) || c == '/'; } ) == 0; +} + // start of consensus code @@ -156,8 +157,9 @@ CScript prices_betopret(CPubKey mypk,int32_t height,int64_t amount,int16_t lever uint8_t prices_betopretdecode(CScript scriptPubKey,CPubKey &pk,int32_t &height,int64_t &amount,int16_t &leverage,int64_t &firstprice,std::vector &vec,uint256 &tokenid) { std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B' ) + if (vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B') { return(f); } @@ -200,53 +202,409 @@ uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKe return(0); } -CScript prices_finalopret(uint256 bettxid,int64_t profits,int32_t height,CPubKey mypk,int64_t firstprice,int64_t costbasis,int64_t addedbets,int64_t positionsize,int16_t leverage) +CScript prices_finalopret(bool isRekt, uint256 bettxid, CPubKey pk, int32_t lastheight, int64_t costbasis, int64_t lastprice, int64_t liquidationprice, int64_t equity, int64_t exitfee, uint32_t nonce) { CScript opret; - opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'F' << bettxid << profits << height << mypk << firstprice << costbasis << addedbets << positionsize << leverage); + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << (isRekt ? 'R' : 'F') << bettxid << pk << lastheight << costbasis << lastprice << liquidationprice << equity << exitfee << nonce); return(opret); } -uint8_t prices_finalopretdecode(CScript scriptPubKey,uint256 &bettxid,int64_t &profits,int32_t &height,CPubKey &pk,int64_t &firstprice,int64_t &costbasis,int64_t &addedbets,int64_t &positionsize,int16_t &leverage) +uint8_t prices_finalopretdecode(CScript scriptPubKey, uint256 &bettxid, CPubKey &pk, int32_t &lastheight, int64_t &costbasis, int64_t &lastprice, int64_t &liquidationprice, int64_t &equity, int64_t &exitfee) { std::vector vopret; uint8_t e,f; + uint32_t nonce; + GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> profits; ss >> height; ss >> pk; ss >> firstprice; ss >> costbasis; ss >> addedbets; ss >> positionsize; ss >> leverage) != 0 && e == EVAL_PRICES && f == 'F' ) + if (vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> bettxid; ss >> pk; ss >> lastheight; ss >> costbasis; ss >> lastprice; ss >> liquidationprice; ss >> equity; ss >> exitfee; if (!ss.eof()) ss >> nonce; ) != 0 && e == EVAL_PRICES && (f == 'F' || f == 'R')) { return(f); } return(0); } +// price opret basic validation and retrieval +static uint8_t PricesCheckOpret(const CTransaction & tx, vscript_t &opret) +{ + if (tx.vout.size() > 0 && GetOpReturnData(tx.vout.back().scriptPubKey, opret) && opret.size() > 2 && opret.begin()[0] == EVAL_PRICES && IS_CHARINSTR(opret.begin()[1], "BACFR")) + return opret.begin()[1]; + else + return (uint8_t)0; +} + +// validate bet tx helper +static bool ValidateBetTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & bettx) +{ + uint256 tokenid; + int64_t positionsize, firstprice; + int32_t firstheight; + int16_t leverage; + CPubKey pk, pricespk; + std::vector vec; + + // check payment cc config: + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + GetKomodoEarlytxidScriptPub(); + + if (bettx.vout.size() < 6 || bettx.vout.size() > 7) + return eval->Invalid("incorrect vout number for bet tx"); + + vscript_t opret; + if( prices_betopretdecode(bettx.vout.back().scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) != 'B') + return eval->Invalid("cannot decode opreturn for bet tx"); + + pricespk = GetUnspendable(cp, 0); + + if (MakeCC1vout(cp->evalcode, bettx.vout[0].nValue, pk) != bettx.vout[0]) + return eval->Invalid("cannot validate vout0 in bet tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, bettx.vout[1].nValue, pricespk) != bettx.vout[1]) + return eval->Invalid("cannot validate vout1 in bet tx with global pk"); + if (MakeCC1vout(cp->evalcode, bettx.vout[2].nValue, pricespk) != bettx.vout[2] ) + return eval->Invalid("cannot validate vout2 in bet tx with pk from opreturn"); + // This should be all you need to verify it, maybe also check amount? + if ( bettx.vout[4].scriptPubKey != KOMODO_EARLYTXID_SCRIPTPUB ) + return eval->Invalid("the fee was paid to wrong address."); + + int64_t betamount = bettx.vout[2].nValue; + if (betamount != PRICES_SUBREVSHAREFEE(positionsize)) { + return eval->Invalid("invalid position size in the opreturn"); + } + + // validate if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey) + CAmount ccOutputs = 0; + for (auto vout : bettx.vout) + if (vout.scriptPubKey.IsPayToCryptoCondition()) + ccOutputs += vout.nValue; + CAmount normalInputs = TotalPubkeyNormalInputs(bettx, pk); + if (normalInputs < ccOutputs) { + return eval->Invalid("bettx normal inputs not signed with pubkey in opret"); + } + + if (leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE) { + return eval->Invalid("invalid leverage"); + } + + return true; +} + +// validate add funding tx helper +static bool ValidateAddFundingTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & addfundingtx, const CTransaction & vintx) +{ + uint256 bettxid; + int64_t amount; + CPubKey pk, pricespk; + vscript_t vintxOpret; + + // check payment cc config: + if (ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0) + GetKomodoEarlytxidScriptPub(); + + if (addfundingtx.vout.size() < 4 || addfundingtx.vout.size() > 5) + return eval->Invalid("incorrect vout number for add funding tx"); + + vscript_t opret; + if (prices_addopretdecode(addfundingtx.vout.back().scriptPubKey, bettxid, pk, amount) != 'A') + return eval->Invalid("cannot decode opreturn for add funding tx"); + + pricespk = GetUnspendable(cp, 0); + uint8_t vintxFuncId = PricesCheckOpret(vintx, vintxOpret); + if (vintxFuncId != 'A' && vintxFuncId != 'B') { // if vintx is bettx + return eval->Invalid("incorrect vintx funcid"); + } + + if (vintxFuncId == 'B' && vintx.GetHash() != bettxid) {// if vintx is bettx + return eval->Invalid("incorrect bet txid in opreturn"); + } + + if (MakeCC1vout(cp->evalcode, addfundingtx.vout[0].nValue, pk) != addfundingtx.vout[0]) + return eval->Invalid("cannot validate vout0 in add funding tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, addfundingtx.vout[1].nValue, pricespk) != addfundingtx.vout[1]) + return eval->Invalid("cannot validate vout1 in add funding tx with global pk"); + + // This should be all you need to verify it, maybe also check amount? + if (addfundingtx.vout[2].scriptPubKey != KOMODO_EARLYTXID_SCRIPTPUB) + return eval->Invalid("the fee was paid to wrong address."); + + int64_t betamount = addfundingtx.vout[1].nValue; + if (betamount != PRICES_SUBREVSHAREFEE(amount)) { + return eval->Invalid("invalid bet position size in the opreturn"); + } + + return true; +} + +// validate costbasis tx helper (deprecated) +/* +static bool ValidateCostbasisTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & costbasistx, const CTransaction & bettx) +{ + uint256 bettxid; + int64_t costbasisInOpret; + CPubKey pk, pricespk; + int32_t height; + + return true; //deprecated + + // check basic structure: + if (costbasistx.vout.size() < 3 || costbasistx.vout.size() > 4) + return eval->Invalid("incorrect vout count for costbasis tx"); + + vscript_t opret; + if (prices_costbasisopretdecode(costbasistx.vout.back().scriptPubKey, bettxid, pk, height, costbasisInOpret) != 'C') + return eval->Invalid("cannot decode opreturn for costbasis tx"); + + pricespk = GetUnspendable(cp, 0); + if (CTxOut(costbasistx.vout[0].nValue, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG) != costbasistx.vout[0]) //might go to any pk who calculated costbasis + return eval->Invalid("cannot validate vout0 in costbasis tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, costbasistx.vout[1].nValue, pricespk) != costbasistx.vout[1]) + return eval->Invalid("cannot validate vout1 in costbasis tx with global pk"); + + if (bettx.GetHash() != bettxid) + return eval->Invalid("incorrect bettx id"); + + if (bettx.vout.size() < 1) // for safety and for check encapsulation + return eval->Invalid("incorrect bettx no vouts"); + + // check costbasis rules: + if (costbasistx.vout[0].nValue > bettx.vout[1].nValue / 10) { + return eval->Invalid("costbasis myfee too big"); + } + + uint256 tokenid; + int64_t positionsize, firstprice; + int32_t firstheight; + int16_t leverage; + CPubKey betpk; + std::vector vec; + if (prices_betopretdecode(bettx.vout.back().scriptPubKey, betpk, firstheight, positionsize, leverage, firstprice, vec, tokenid) != 'B') + return eval->Invalid("cannot decode opreturn for bet tx"); + + if (firstheight + PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH > chainActive.Height()) { + return eval->Invalid("cannot calculate costbasis yet"); + } + + int64_t costbasis = 0, profits, lastprice; + int32_t retcode = prices_syntheticprofits(costbasis, firstheight, firstheight + PRICES_DAYWINDOW, leverage, vec, positionsize, profits, lastprice); + if (retcode < 0) + return eval->Invalid("cannot calculate costbasis yet"); + std::cerr << "ValidateCostbasisTx() costbasis=" << costbasis << " costbasisInOpret=" << costbasisInOpret << std::endl; + if (costbasis != costbasisInOpret) { + //std::cerr << "ValidateBetTx() " << "incorrect costbasis value" << std::endl; + return eval->Invalid("incorrect costbasis value"); + } + + return true; +} +*/ + +// validate final tx helper +static bool ValidateFinalTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & finaltx, const CTransaction & bettx) +{ + uint256 bettxid; + int64_t amount; + CPubKey pk, pricespk; + int64_t profits; + int32_t lastheight; + int64_t firstprice, costbasis, lastprice, liquidationprice, equity, fee; + int16_t leverage; + + if (finaltx.vout.size() < 3 || finaltx.vout.size() > 4) { + //std::cerr << "ValidateFinalTx()" << " incorrect vout number for final tx =" << finaltx.vout.size() << std::endl; + return eval->Invalid("incorrect vout number for final tx"); + } + + vscript_t opret; + uint8_t funcId; + if ((funcId = prices_finalopretdecode(finaltx.vout.back().scriptPubKey, bettxid, pk, lastheight, costbasis, lastprice, liquidationprice, equity, fee)) == 0) + return eval->Invalid("cannot decode opreturn for final tx"); + + // check rekt txid mining: +// if( funcId == 'R' && (finaltx.GetHash().begin()[0] != 0 || finaltx.GetHash().begin()[31] != 0) ) +// return eval->Invalid("incorrect rekt txid"); + + if (bettx.GetHash() != bettxid) + return eval->Invalid("incorrect bettx id"); + + pricespk = GetUnspendable(cp, 0); + + if (CTxOut(finaltx.vout[0].nValue, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG) != finaltx.vout[0]) + return eval->Invalid("cannot validate vout0 in final tx with pk from opreturn"); + + if( finaltx.vout.size() == 3 && MakeCC1vout(cp->evalcode, finaltx.vout[1].nValue, pricespk) != finaltx.vout[1] ) + return eval->Invalid("cannot validate vout1 in final tx with global pk"); + + // TODO: validate exitfee for 'R' + // TODO: validate amount for 'F' + + return true; +} + +// validate prices tx function +// performed checks: +// basic tx structure (vout num) +// basic tx opret structure +// reference to the bet tx vout +// referenced bet txid in tx opret +// referenced bet tx structure +// non-final tx has only 1 cc vin +// cc vouts to self with mypubkey from opret +// cc vouts to global pk with global pk +// for bet tx that normal inputs digned with my pubkey from the opret >= cc outputs - disable betting for other pubkeys (Do we need this rule?) +// TODO: +// opret params (firstprice,positionsize...) +// costbasis calculation +// cashout balance (PricesExactAmounts) +// use the special address for 50% fees bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { + vscript_t vopret; + + if (strcmp(ASSETCHAINS_SYMBOL, "REKT0") == 0 && chainActive.Height() < 5851) + return true; + // check basic opret rules: + if (PricesCheckOpret(tx, vopret) == 0) + return eval->Invalid("tx has no prices opreturn"); + + uint8_t funcId = vopret.begin()[1]; + + CTransaction firstVinTx; + vscript_t firstVinTxOpret; + bool foundFirst = false; + int32_t ccVinCount = 0; + uint32_t prevCCoutN = 0; + + // check basic rules: + + // find first cc vin and load vintx (might be either bet or add funding tx): + for (auto vin : tx.vin) { + if (cp->ismyvin(vin.scriptSig)) { + CTransaction vintx; + uint256 hashBlock; + vscript_t vintxOpret; + + if (!myGetTransaction(vin.prevout.hash, vintx, hashBlock)) + return eval->Invalid("cannot load vintx"); + + if (PricesCheckOpret(vintx, vintxOpret) == 0) { + //return eval->Invalid("cannot find prices opret in vintx"); + std::cerr << "PricesValidate() " << "cannot find prices opret in vintx" << std::endl; + } + + if (!IS_CHARINSTR(funcId, "FR") && vintxOpret.begin()[1] == 'B' && prevCCoutN == 1) { + //return eval->Invalid("cannot spend bet marker"); + std::cerr << "PricesValidate() " << " non-final tx cannot spend cc marker vout=" << prevCCoutN << std::endl; + } + + if (!foundFirst) { + prevCCoutN = vin.prevout.n; + firstVinTx = vintx; + firstVinTxOpret = vintxOpret; + foundFirst = true; + } + ccVinCount++; + } + } + if (!foundFirst) + return eval->Invalid("prices cc vin not found"); + + if (!IS_CHARINSTR(funcId, "FR") && ccVinCount > 1) {// for all prices tx except final tx only one cc vin is allowed + //return eval->Invalid("only one prices cc vin allowed for this tx"); + std::cerr << "PricesValidate() " << "only one prices cc vin allowed for this tx" << std::endl; + } + + switch (funcId) { + case 'B': // bet + return eval->Invalid("unexpected validate for bet funcid"); + + case 'A': // add funding + // check tx structure: + if (!ValidateAddFundingTx(cp, eval, tx, firstVinTx)) { + std::cerr << "PricesValidate() " << "ValidateAddFundingTx = false " << eval->state.GetRejectReason() << std::endl; + return false; // invalid state is already set in the func + } + + if (firstVinTxOpret.begin()[1] == 'B') { + if (!ValidateBetTx(cp, eval, firstVinTx)) {// check tx structure + std::cerr << "PricesValidate() " << "funcId=A ValidatebetTx = false " << eval->state.GetRejectReason() << std::endl; + return false; // invalid state is already set in the func + } + } + + if (prevCCoutN != 0) { // check spending rules + std::cerr << "PricesValidate() " << "addfunding tx incorrect vout to spend=" << prevCCoutN << std::endl; + return eval->Invalid("incorrect vintx vout to spend"); + } + break; + + /* not used: + case 'C': // set costbasis + if (!ValidateCostbasisTx(cp, eval, tx, firstVinTx)) { + //return false; + std::cerr << "PricesValidate() " << "ValidateCostbasisTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (!ValidateBetTx(cp, eval, firstVinTx)) { + //return false; + std::cerr << "PricesValidate() " << "funcId=C ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (prevoutN != 1) { // check spending rules + // return eval->Invalid("incorrect vout to spend"); + std::cerr << "PricesValidate() " << "costbasis tx incorrect vout to spend=" << prevoutN << std::endl; + } + //return eval->Invalid("test: costbasis is good"); + break; */ + + case 'F': // final tx + case 'R': + if (!ValidateFinalTx(cp, eval, tx, firstVinTx)) { + std::cerr << "PricesValidate() " << "ValidateFinalTx=false " << eval->state.GetRejectReason() << std::endl; + return false; + } + if (!ValidateBetTx(cp, eval, firstVinTx)) { + std::cerr << "PricesValidate() " << "ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl; + return false; + } + if (prevCCoutN != 1) { // check spending rules + std::cerr << "PricesValidate() "<< "final tx incorrect vout to spend=" << prevCCoutN << std::endl; + return eval->Invalid("incorrect vout to spend"); + } + break; + + default: + return eval->Invalid("invalid funcid"); + } + + eval->state = CValidationState(); return true; } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,int64_t total,int32_t maxinputs,uint256 vintxid,int32_t vinvout) +int64_t AddPricesInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, char *destaddr, int64_t total, int32_t maxinputs) { - int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + int64_t nValue, price, totalinputs = 0; uint256 txid, hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout, n = 0; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,destaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + + SetCCunspents(unspentOutputs, destaddr); + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( vout == vinvout && txid == vintxid ) - continue; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) + //if (vout == exclvout && txid == excltxid) // exclude vout which is added directly to vins outside this function + // continue; + if (GetTransaction(txid, vintx, hashBlock, false) != 0 && vout < vintx.vout.size()) { - if ( (nValue= vintx.vout[vout].nValue) >= total/maxinputs && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + vscript_t vopret; + uint8_t funcId = PricesCheckOpret(vintx, vopret); + if (funcId == 'B' && vout == 1) // skip cc marker + continue; + + if ((nValue = vintx.vout[vout].nValue) >= total / maxinputs && myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, vout) == 0) { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); + if (total != 0 && maxinputs != 0) + mtx.vin.push_back(CTxIn(txid, vout, CScript())); nValue = it->second.satoshis; totalinputs += nValue; n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; } } @@ -254,474 +612,1909 @@ int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char return(totalinputs); } -UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +// return min equity percentage depending on leverage value +// for lev=1 2% +// for lev>=100 10% +double prices_minmarginpercent(int16_t leverage) +{ + int16_t absleverage = std::abs(leverage); + if (absleverage < 100) + return (absleverage * 0.080808 + 1.9191919) / 100.0; + else + return 0.1; +} + + +UniValue prices_rawtxresult(UniValue &result, std::string rawtx, int32_t broadcastflag) { CTransaction tx; - if ( rawtx.size() > 0 ) + if (rawtx.size() > 0) { - result.push_back(Pair("hex",rawtx)); - if ( DecodeHexTx(tx,rawtx) != 0 ) + result.push_back(Pair("hex", rawtx)); + if (DecodeHexTx(tx, rawtx) != 0) { - if ( broadcastflag != 0 && myAddtomempool(tx) != 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")); + result.push_back(Pair("txid", tx.GetHash().ToString())); + result.push_back(Pair("result", "success")); + } + else + result.push_back(Pair("error", "decode hex")); + } + else + result.push_back(Pair("error", "couldnt finalize CCtx")); return(result); } -int32_t prices_syntheticvec(std::vector &vec,std::vector synthetic) +static std::string prices_getsourceexpression(const std::vector &vec) { + + std::string expr; + + for (int32_t i = 0; i < vec.size(); i++) + { + char name[65]; + std::string operand; + uint16_t opcode = vec[i]; + int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // index or weight + + switch (opcode & KOMODO_PRICEMASK) + { + case 0: // indices + komodo_pricename(name, value); + operand = std::string(name); + break; + + case PRICES_WEIGHT: // multiply by weight and consume top of stack by updating price + operand = std::to_string(value); + break; + + case PRICES_MULT: // "*" + operand = std::string("*"); + break; + + case PRICES_DIV: // "/" + operand = std::string("/"); + break; + + case PRICES_INV: // "!" + operand = std::string("!"); + break; + + case PRICES_MDD: // "*//" + operand = std::string("*//"); + break; + + case PRICES_MMD: // "**/" + operand = std::string("**/"); + break; + + case PRICES_MMM: // "***" + operand = std::string("***"); + break; + + case PRICES_DDD: // "///" + operand = std::string("///"); + break; + + default: + return "invalid opcode"; + break; + } + + if (expr.size() > 0) + expr += std::string(", "); + expr += operand; + } + return expr; +} + +// helper functions to get synthetic expression reduced: + +// return s true and needed operand count if string is opcode +static bool prices_isopcode(const std::string &s, int &need) { - int32_t i,need,ind,depth = 0; std::string opstr; uint16_t opcode,weight; - if ( synthetic.size() == 0 ) + if (s == "!") { + need = 1; + return true; + } + else if (s == "*" || s == "/") { + need = 2; + return true; + } + else if (s == "***" || s == "///" || s == "*//" || s == "**/") { + need = 3; + return true; + } + else + return false; +} + +// split pair onto two quotes divided by "_" +static void prices_splitpair(const std::string &pair, std::string &upperquote, std::string &bottomquote) +{ + size_t pos = pair.find('_'); // like BTC_USD + if (pos != std::string::npos) { + upperquote = pair.substr(0, pos); + bottomquote = pair.substr(pos + 1); + } + else { + upperquote = pair; + bottomquote = ""; + } + //std::cerr << "prices_splitpair: upperquote=" << upperquote << " bottomquote=" << bottomquote << std::endl; +} + +// invert pair like BTS_USD -> USD_BTC +static std::string prices_invertpair(const std::string &pair) +{ + std::string upperquote, bottomquote; + prices_splitpair(pair, upperquote, bottomquote); + return bottomquote + std::string("_") + upperquote; +} + +// invert pairs in operation accordingly to "/" operator, convert operator to * or *** +static void prices_invertoperation(const std::vector &vexpr, int p, std::vector &voperation) +{ + int need; + + voperation.clear(); + if (prices_isopcode(vexpr[p], need)) { + if (need > 1) { + if (need == 2) { + voperation.push_back(vexpr[p - 2]); + if (vexpr[p] == "/") + voperation.push_back(prices_invertpair(vexpr[p - 1])); + else + voperation.push_back(vexpr[p - 1]); + voperation.push_back("*"); + } + + if (need == 3) { + int i; + std::string::const_iterator c; + for (c = vexpr[p].begin(), i = -3; c != vexpr[p].end(); c++, i++) { + if (*c == '/') + voperation.push_back(prices_invertpair(vexpr[p + i])); + else + voperation.push_back(vexpr[p + i]); + } + voperation.push_back("***"); + } + } + else if (vexpr[p] == "!") { + voperation.push_back(prices_invertpair(vexpr[p - 1])); + // do not add operator + } + } + + //std::cerr << "prices_invert inverted="; + //for (auto v : voperation) std::cerr << v << " "; + //std::cerr << std::endl; +} + +// reduce pairs in the operation, change or remove opcode if reduced +static int prices_reduceoperands(std::vector &voperation) +{ + int opcount = voperation.size() - 1; + int need = opcount; + //std::cerr << "prices_reduceoperands begin need=" << need << std::endl; + + while (true) { + int i; + //std::cerr << "prices_reduceoperands opcount=" << opcount << std::endl; + for (i = 0; i < opcount; i++) { + std::string upperquote, bottomquote; + bool breaktostart = false; + + //std::cerr << "prices_reduceoperands voperation[i]=" << voperation[i] << " i=" << i << std::endl; + prices_splitpair(voperation[i], upperquote, bottomquote); + if (upperquote == bottomquote) { + std::cerr << "prices_reduceoperands erasing i=" << i << std::endl; + voperation.erase(voperation.begin() + i); + opcount--; + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; + + if (voperation.size() > 0 && voperation.back() == "*") + voperation.pop_back(); + breaktostart = true; + break; + } + + + int j; + for (j = i + 1; j < opcount; j++) { + + //std::cerr << "prices_reduceoperands voperation[j]=" << voperation[j] << " j=" << j << std::endl; + + std::string upperquotej, bottomquotej; + prices_splitpair(voperation[j], upperquotej, bottomquotej); + if (upperquote == bottomquotej || bottomquote == upperquotej) { + if (upperquote == bottomquotej) + voperation[i] = upperquotej + "_" + bottomquote; + else + voperation[i] = upperquote + "_" + bottomquotej; + //std::cerr << "prices_reduceoperands erasing j=" << j << std::endl; + voperation.erase(voperation.begin() + j); + opcount--; + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; + + need--; + if (voperation.back() == "***") { + voperation.pop_back(); + voperation.push_back("*"); // convert *** to * + } + else if (voperation.back() == "*") { + voperation.pop_back(); // convert * to nothing + } + breaktostart = true; + break; + } + } + if (breaktostart) + break; + } + if (i >= opcount) // all seen + break; + } + + //std::cerr << "prices_reduceoperands end need=" << need << std::endl; + return need; +} + +// substitute reduced operation in vectored expr +static void prices_substitutereduced(std::vector &vexpr, int p, std::vector voperation) +{ + int need; + if (prices_isopcode(vexpr[p], need)) { + vexpr.erase(vexpr.begin() + p - need, vexpr.begin() + p + 1); + vexpr.insert(vexpr.begin() + p - need, voperation.begin(), voperation.end()); + } +} + +// try to reduce synthetic expression by substituting "BTC_USD, BTC_EUR, 30, /" with "EUR_USD, 30" etc +static std::string prices_getreducedexpr(const std::string &expr) +{ + std::string reduced; + + std::vector vexpr; + SplitStr(expr, vexpr); + + for (size_t i = 0; i < vexpr.size(); i++) { + int need; + + if (prices_isopcode(vexpr[i], need)) { + std::vector voperation; + prices_invertoperation(vexpr, i, voperation); + if (voperation.size() > 0) { + int reducedneed = prices_reduceoperands(voperation); + if (reducedneed < need) { + prices_substitutereduced(vexpr, i, voperation); + } + } + } + } + + for (size_t i = 0; i < vexpr.size(); i++) { + if (reduced.size() > 0) + reduced += std::string(", "); + reduced += vexpr[i]; + } + + //std::cerr << "reduced=" << reduced << std::endl; + return reduced; +} + +// parse synthetic expression into vector of codes +int32_t prices_syntheticvec(std::vector &vec, std::vector synthetic) +{ + int32_t i, need, ind, depth = 0; std::string opstr; uint16_t opcode, weight; + if (synthetic.size() == 0) { + std::cerr << "prices_syntheticvec() expression is empty" << std::endl; return(-1); - for (i=0; i= 0 ) + else if (!is_weight_str(opstr) && (ind = komodo_priceind(opstr.c_str())) >= 0) opcode = ind, need = 0; - else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) + else if ((weight = atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES) { opcode = PRICES_WEIGHT | weight; need = 1; - } else return(-2); - if ( depth < need ) + } + else { + std::cerr << "prices_syntheticvec() incorrect opcode=" << opstr << std::endl; + return(-2); + } + if (depth < need) { + std::cerr << "prices_syntheticvec() incorrect not enough operands for opcode=" << opstr << std::endl; return(-3); + } depth -= need; - if ( (opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT ) // weight - depth++; - if ( depth > 3 ) + ///std::cerr << "prices_syntheticvec() opcode=" << opcode << " opstr=" << opstr << " need=" << need << " depth=" << depth << std::endl; + if ((opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT) { // skip weight + depth++; // increase operands count + ///std::cerr << "depth++=" << depth << std::endl; + } + if (depth > 3) { + std::cerr << "prices_syntheticvec() too many operands, last=" << opstr << std::endl; return(-4); + } vec.push_back(opcode); } - if ( depth != 0 ) + if (depth != 0) { - fprintf(stderr,"depth.%d not empty\n",depth); + fprintf(stderr, "prices_syntheticvec() depth.%d not empty\n", depth); return(-5); } return(0); } -int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) +// calculates price for synthetic expression +int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t minmax, int16_t leverage) { - int32_t i,ind,errcode,depth,retval = -1; uint16_t opcode; int64_t pricedata[PRICES_MAXDATAPOINTS],pricestack[4],price,den,a,b,c; - price = den = depth = errcode = 0; - for (i=0; i= 2) { + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * b) / SATOSHIDEN; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); + + } + else + errcode = -3; + break; + + case PRICES_DIV: // "/" + if (depth >= 2) { + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * SATOSHIDEN) / b; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -4; + break; + + case PRICES_INV: // "!" + if (depth >= 1) { + a = pricestack[--depth]; + // pricestack[depth++] = (SATOSHIDEN * SATOSHIDEN) / a; + mpz_set_si(mpzA, a); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzA); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -5; + break; + + case PRICES_MDD: // "*//" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (((a * SATOSHIDEN) / b) * SATOSHIDEN) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -6; + break; + + case PRICES_MMD: // "**/" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * b) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -7; + break; + + case PRICES_MMM: // "***" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (((a * b) / SATOSHIDEN ) * c) / SATOSHIDEN; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_mul(mpzResult, mpzResult, mpzC); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -8; + break; + + case PRICES_DDD: // "///" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + //pricestack[depth++] = (((((SATOSHIDEN * SATOSHIDEN) / a) * SATOSHIDEN) / b) * SATOSHIDEN) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzA); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -9; + break; + + default: + errcode = -10; + break; + } + + // std::cerr << "prices_syntheticprice test mpzResult=" << mpz_get_si(mpzResult) << std::endl; + // check overflow: + if (mpz_cmp_si(mpzResult, std::numeric_limits::max()) > 0) { + errcode = -13; + break; + } + + if (errcode != 0) + break; + + // if( depth > 0 ) + // std::cerr << "prices_syntheticprice top pricestack[depth-1=" << depth-1 << "]=" << pricestack[depth-1] << std::endl; + // else + // std::cerr << "prices_syntheticprice pricestack empty" << std::endl; + } - if ( den == 0 ) + free(pricedata); + mpz_clear(mpzResult); + mpz_clear(mpzA); + mpz_clear(mpzB); + mpz_clear(mpzC); + + if( mpz_get_si(mpzDen) != 0 ) + mpz_tdiv_q(mpzTotalPrice, mpzTotalPrice, mpzDen); // price / den + + int64_t den = mpz_get_si(mpzDen); + int64_t priceIndex = mpz_get_si(mpzTotalPrice); + + mpz_clear(mpzDen); + mpz_clear(mpzTotalPrice); + mpz_clear(mpzPriceValue); + + if (errcode != 0) + std::cerr << "prices_syntheticprice errcode in switch=" << errcode << std::endl; + + if( errcode == -1 ) { + std::cerr << "prices_syntheticprice error getting price (could be end of chain)" << std::endl; + return errcode; + } + + if (errcode == -13) { + std::cerr << "prices_syntheticprice overflow in price" << std::endl; + return errcode; + } + if (errcode == -14) { + std::cerr << "prices_syntheticprice price is zero, not enough historic data yet" << std::endl; + return errcode; + } + if (den == 0) { + std::cerr << "prices_syntheticprice den==0 return err=-11" << std::endl; return(-11); - else if ( depth != 0 ) + } + else if (depth != 0) { + std::cerr << "prices_syntheticprice depth!=0 err=-12" << std::endl; return(-12); - else if ( errcode != 0 ) + } + else if (errcode != 0) { + std::cerr << "prices_syntheticprice err=" << errcode << std::endl; return(errcode); - return(price / den); -} - -int64_t prices_syntheticprofits(int64_t &costbasis,int32_t firstheight,int32_t height,int16_t leverage,std::vector vec,int64_t positionsize,int64_t addedbets) -{ - int64_t price,profits = 0; int32_t minmax; - minmax = (height > firstheight+PRICES_DAYWINDOW); - if ( (price= prices_syntheticprice(vec,height,minmax,leverage)) < 0 ) - { - fprintf(stderr,"unexpected zero synthetic price at height.%d\n",height); - return(0); } - if ( minmax != 0 ) - { - if ( leverage > 0 && price > costbasis ) - costbasis = price; - else if ( leverage < 0 && (costbasis == 0 || price < costbasis) ) - costbasis = price; - } - profits = ((price * SATOSHIDEN) / costbasis) - SATOSHIDEN; - profits *= leverage * positionsize; - return(positionsize + addedbets + profits); +// std::cerr << "prices_syntheticprice priceIndex=totalprice/den=" << priceIndex << " den=" << den << std::endl; + + return priceIndex; } -void prices_betjson(UniValue &result,int64_t profits,int64_t costbasis,int64_t positionsize,int64_t addedbets,int16_t leverage,int32_t firstheight,int64_t firstprice) +// calculates costbasis and profit/loss for the bet +int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t height, int16_t leverage, std::vector vec, int64_t positionsize, int64_t &profits, int64_t &outprice) { - result.push_back(Pair("profits",ValueFromAmount(profits))); - result.push_back(Pair("costbasis",ValueFromAmount(costbasis))); - result.push_back(Pair("positionsize",ValueFromAmount(positionsize))); - result.push_back(Pair("addedbets",ValueFromAmount(addedbets))); - result.push_back(Pair("leverage",(int64_t)leverage)); - result.push_back(Pair("firstheight",(int64_t)firstheight)); - result.push_back(Pair("firstprice",ValueFromAmount(firstprice))); + int64_t price; +#ifndef TESTMODE + const int32_t COSTBASIS_PERIOD = PRICES_DAYWINDOW; +#else + const int32_t COSTBASIS_PERIOD = 7; +#endif + + + if (height < firstheight) { + fprintf(stderr, "requested height is lower than bet firstheight.%d\n", height); + return -1; + } + + int32_t minmax = (height < firstheight + COSTBASIS_PERIOD); // if we are within 24h then use min or max value + + if ((price = prices_syntheticprice(vec, height, minmax, leverage)) < 0) + { + fprintf(stderr, "error getting synthetic price at height.%d\n", height); + return -1; + } + + // clear lowest positions: + //price /= PRICES_POINTFACTOR; + //price *= PRICES_POINTFACTOR; + outprice = price; + + if (minmax) { // if we are within day window, set temp costbasis to max (or min) price value + if (leverage > 0 && price > costbasis) { + costbasis = price; // set temp costbasis + //std::cerr << "prices_syntheticprofits() minmax costbasis=" << costbasis << std::endl; + } + else if (leverage < 0 && (costbasis == 0 || price < costbasis)) { + costbasis = price; + //std::cerr << "prices_syntheticprofits() minmax costbasis=" << costbasis << std::endl; + } + //else { //-> use the previous value + // std::cerr << "prices_syntheticprofits() unchanged costbasis=" << costbasis << " price=" << price << " leverage=" << leverage << std::endl; + //} + } + else { + //if (height == firstheight + COSTBASIS_PERIOD) { + // if costbasis not set, just set it + //costbasis = price; + + // use calculated minmax costbasis + //std::cerr << "prices_syntheticprofits() use permanent costbasis=" << costbasis << " at height=" << height << std::endl; + //} + } + + // normalize to 10,000,000 to prevent underflow + //profits = costbasis > 0 ? (((price / PRICES_POINTFACTOR * PRICES_NORMFACTOR) / costbasis) - PRICES_NORMFACTOR / PRICES_POINTFACTOR) * PRICES_POINTFACTOR : 0; + //double dprofits = costbasis > 0 ? ((double)price / (double)costbasis - 1) : 0; + + //std::cerr << "prices_syntheticprofits() test value1 (price/PRICES_POINTFACTOR * PRICES_NORMFACTOR)=" << (price / PRICES_POINTFACTOR * PRICES_NORMFACTOR) << std::endl; + //std::cerr << "prices_syntheticprofits() test value2 (price/PRICES_POINTFACTOR * PRICES_NORMFACTOR)/costbasis=" << (costbasis != 0 ? (price / PRICES_POINTFACTOR * PRICES_NORMFACTOR)/costbasis : 0) << std::endl; + + //std::cerr << "prices_syntheticprofits() fractional profits=" << profits << std::endl; + //std::cerr << "prices_syntheticprofits() profits double=" << (double)price / (double)costbasis -1.0 << std::endl; + //double dprofits = (double)price / (double)costbasis - 1.0; + + //profits *= ((int64_t)leverage * (int64_t)positionsize); + //profits /= (int64_t)PRICES_NORMFACTOR; // de-normalize + + //dprofits *= ((double)leverage * (double)positionsize); + + //dprofits *= leverage * positionsize; + // profits = dprofits; + //std::cerr << "prices_syntheticprofits() dprofits=" << dprofits << std::endl; + + if (costbasis > 0) { + mpz_t mpzProfits; + mpz_t mpzCostbasis; + mpz_t mpzPrice; + mpz_t mpzLeverage; + + mpz_init(mpzProfits); + mpz_init(mpzCostbasis); + mpz_init(mpzPrice); + mpz_init(mpzLeverage); + + mpz_set_si(mpzCostbasis, costbasis); + mpz_set_si(mpzPrice, price); + mpz_mul_ui(mpzPrice, mpzPrice, SATOSHIDEN); // (price*SATOSHIDEN) + + mpz_tdiv_q(mpzProfits, mpzPrice, mpzCostbasis); // profits = (price*SATOSHIDEN)/costbasis // normalization + mpz_sub_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits -= SATOSHIDEN + + mpz_set_si(mpzLeverage, leverage); + mpz_mul(mpzProfits, mpzProfits, mpzLeverage); // profits *= leverage + mpz_mul_ui(mpzProfits, mpzProfits, positionsize); // profits *= positionsize + mpz_tdiv_q_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits /= SATOSHIDEN // de-normalization + + profits = mpz_get_si(mpzProfits); + + mpz_clear(mpzLeverage); + mpz_clear(mpzProfits); + mpz_clear(mpzCostbasis); + mpz_clear(mpzPrice); + + } + else + profits = 0; + + //std::cerr << "prices_syntheticprofits() profits=" << profits << std::endl; + return 0; // (positionsize + addedbets + profits); } -int64_t prices_costbasis(CTransaction bettx) +// makes result json object +void prices_betjson(UniValue &result, std::vector bets, int16_t leverage, int32_t endheight, int64_t lastprice) +{ + + UniValue resultbets(UniValue::VARR); + int64_t totalposition = 0; + int64_t totalprofits = 0; + + for (auto b : bets) { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("positionsize", ValueFromAmount(b.positionsize))); + entry.push_back(Pair("profits", ValueFromAmount(b.profits))); + entry.push_back(Pair("costbasis", ValueFromAmount(b.costbasis))); + entry.push_back(Pair("firstheight", b.firstheight)); + resultbets.push_back(entry); + totalposition += b.positionsize; + totalprofits += b.profits; + } + int64_t equity = totalposition + totalprofits; + + result.push_back(Pair("bets", resultbets)); + result.push_back(Pair("leverage", (int64_t)leverage)); + result.push_back(Pair("TotalPositionSize", ValueFromAmount(totalposition))); + result.push_back(Pair("TotalProfits", ValueFromAmount(totalprofits))); + result.push_back(Pair("equity", ValueFromAmount(equity))); + result.push_back(Pair("LastPrice", ValueFromAmount(lastprice))); + result.push_back(Pair("LastHeight", endheight)); +} + +// retrieves costbasis from a tx spending bettx vout1 (deprecated) +int64_t prices_costbasis(CTransaction bettx, uint256 &txidCostbasis) { int64_t costbasis = 0; // if vout1 is spent, follow and extract costbasis from opreturn //uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) + //uint256 txidCostbasis; + int32_t vini; + int32_t height; + txidCostbasis = zeroid; +/* + if (CCgetspenttxid(txidCostbasis, vini, height, bettx.GetHash(), 1) < 0) { + std::cerr << "prices_costbasis() no costbasis txid found" << std::endl; + return 0; + } - return(costbasis); + CTransaction txCostbasis; + uint256 hashBlock; + uint256 bettxid; + CPubKey pk; + bool isLoaded = false; + uint8_t funcId = 0; + + if ((isLoaded = myGetTransaction(txidCostbasis, txCostbasis, hashBlock)) && + txCostbasis.vout.size() > 0 && + (funcId = prices_costbasisopretdecode(txCostbasis.vout.back().scriptPubKey, bettxid, pk, height, costbasis)) != 0) { + return costbasis; + } + + std::cerr << "prices_costbasis() cannot load costbasis tx or decode opret" << " isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl; */ + return 0; } -int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid) +// enumerates and retrieves added bets, returns the last baton txid +int64_t prices_enumaddedbets(uint256 &batontxid, std::vector &bets, uint256 bettxid) { - int64_t addedbets = 0; + int64_t addedBetsTotal = 0; + int32_t vini; + int32_t height; + int32_t retcode; + + batontxid = bettxid; // initially set to the source bet tx + uint256 sourcetxid = bettxid; + // iterate through batons, adding up vout1 -> addedbets - return(addedbets); + while ((retcode = CCgetspenttxid(batontxid, vini, height, sourcetxid, 0)) == 0) { + + CTransaction txBaton; + CBlockIndex blockIdx; + uint256 bettxidInOpret; + CPubKey pk; + bool isLoaded = false; + uint8_t funcId = 0; + int64_t amount; + EvalRef eval; + + if ((isLoaded = eval->GetTxConfirmed(batontxid, txBaton, blockIdx)) && + blockIdx.IsValid() && + txBaton.vout.size() > 0 && + (funcId = prices_addopretdecode(txBaton.vout.back().scriptPubKey, bettxidInOpret, pk, amount)) != 0) + { + OneBetData added; + + addedBetsTotal += amount; + added.positionsize = amount; + added.firstheight = blockIdx.GetHeight(); //TODO: check if this is correct (to get height from the block not from the opret) + bets.push_back(added); + //std::cerr << "prices_batontxid() added amount=" << amount << std::endl; + } + else { + std::cerr << "prices_batontxid() cannot load or decode add bet tx, isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl; + return -1; + } + sourcetxid = batontxid; + } + + return(addedBetsTotal); } -UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) +// pricesbet rpc impl: make betting tx +UniValue PricesBet(int64_t txfee, int64_t amount, int16_t leverage, std::vector synthetic) { int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; char myaddr[64]; std::string rawtx; - if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","leverage too big")); - return(result); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = PRICES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( prices_syntheticvec(vec,synthetic) < 0 || (firstprice= prices_syntheticprice(vec,nextheight-1,1,leverage)) < 0 || vec.size() == 0 || vec.size() > 4096 ) - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","invalid synthetic")); - return(result); - } - if ( AddNormalinputs(mtx,mypk,amount+4*txfee,64) >= amount+4*txfee ) - { - betamount = (amount * 199) / 200; - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding - mtx.vout.push_back(MakeCC1vout(cp->evalcode,(amount-betamount)+2*txfee,pricespk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,betamount,pricespk,mypk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_betopret(mypk,nextheight-1,amount,leverage,firstprice,vec,zeroid)); - return(prices_rawtxresult(result,rawtx,0)); - } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); - return(result); -} + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CPubKey pricespk, mypk; + int64_t betamount, firstprice = 0; + std::vector vec; + //char myaddr[64]; + std::string rawtx; -UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) -{ - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + if (leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "leverage too big")); + return(result); + } + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) + pricespk = GetUnspendable(cp, 0); + //GetCCaddress(cp, myaddr, mypk); + if (prices_syntheticvec(vec, synthetic) < 0 || (firstprice = prices_syntheticprice(vec, nextheight - 1, 1, leverage)) < 0 || vec.size() == 0 || vec.size() > 4096) { - if ( prices_batontxid(batontxid,bettx,bettxid) >= 0 ) + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid synthetic")); + return(result); + } + + if (!prices_isacceptableamount(vec, amount, leverage)) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "too big amount and leverage")); + return(result); + } + + if (AddNormalinputs(mtx, mypk, amount + 4 * txfee, 64) >= amount + 4 * txfee) + { + betamount = PRICES_SUBREVSHAREFEE(amount); + + /*if( amount - betamount < PRICES_REVSHAREDUST) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "bet amount too small")); + return(result); + }*/ + + + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, mypk)); // vout0 baton for total funding + // mtx.vout.push_back(MakeCC1vout(cp->evalcode, (amount - betamount) + 2 * txfee, pricespk)); // vout1, when spent, costbasis is set + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, pricespk)); // vout1 cc marker (NVOUT_CCMARKER) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, betamount, pricespk)); // vout2 betamount + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); // vout3 normal marker NVOUT_NORMALMARKER - TODO: remove it as we have cc marker now, when move to the new chain + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) { - mtx.vin.push_back(CTxIn(batontxid,0,CScript())); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding - mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_addopret(bettxid,mypk,amount)); - return(prices_rawtxresult(result,rawtx,0)); + // Lock here, as in validation we cannot call lock in the function itself. + // may not be needed as the validation call to update the global, is called in a LOCK already, and it can only update there and here. + LOCK(cs_main); + GetKomodoEarlytxidScriptPub(); + } + mtx.vout.push_back(CTxOut(amount-betamount, KOMODO_EARLYTXID_SCRIPTPUB)); + //test: mtx.vout.push_back(CTxOut(amount - betamount, CScript() << ParseHex("037c803ec82d12da939ac04379bbc1130a9065c53d8244a61eece1db942cf0efa7") << OP_CHECKSIG)); // vout4 test revshare fee + + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_betopret(mypk, nextheight - 1, amount, leverage, firstprice, vec, zeroid)); + return(prices_rawtxresult(result, rawtx, 0)); + } + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); + return(result); +} + +// pricesaddfunding rpc impl: add yet another bet +UniValue PricesAddFunding(int64_t txfee, uint256 bettxid, int64_t amount) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CTransaction bettx; + CPubKey pricespk, mypk, pk; + int64_t positionsize, betamount, firstprice; + int32_t firstheight; + std::vector vecparsed; + uint256 batontxid, tokenid, hashBlock; + int16_t leverage = 0; + std::string rawtx; + //char myaddr[64]; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp, 0); + + if (!myGetTransaction(bettxid, bettx, hashBlock) || + bettx.vout.size() <= 3 || + hashBlock.IsNull() || + prices_betopretdecode(bettx.vout.back().scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vecparsed, tokenid) != 'B') { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid bet tx")); + return(result); + } + + if (!prices_isacceptableamount(vecparsed, amount, leverage)) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "too big amount and leverage")); + return(result); + } + + if (AddNormalinputs(mtx, mypk, amount + 2*txfee, 64) >= amount + 2*txfee) + { + betamount = PRICES_SUBREVSHAREFEE(amount); + /*if (amount - betamount < PRICES_REVSHAREDUST) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "bet amount too small")); + return(result); + }*/ + + std::vector bets; + if (prices_enumaddedbets(batontxid, bets, bettxid) >= 0) + { + mtx.vin.push_back(CTxIn(batontxid, 0, CScript())); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, mypk)); // vout0 baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode, betamount, pricespk)); // vout1 added amount + + if (ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0) + { + // Lock here, as in validation we cannot call lock in the function itself. + // may not be needed as the validation call to update the global, is called in a LOCK already, and it can only update there and here. + LOCK(cs_main); + GetKomodoEarlytxidScriptPub(); + } + mtx.vout.push_back(CTxOut(amount - betamount, KOMODO_EARLYTXID_SCRIPTPUB)); + // test: mtx.vout.push_back(CTxOut(amount - betamount, CScript() << ParseHex("037c803ec82d12da939ac04379bbc1130a9065c53d8244a61eece1db942cf0efa7") << OP_CHECKSIG)); //vout2 test revshare fee + + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_addopret(bettxid, mypk, amount)); + return(prices_rawtxresult(result, rawtx, 0)); } else { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt find batonttxid")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "couldnt find batonttxid")); return(result); } } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); return(result); } -UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) -{ - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = PRICES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) +// scan chain from the initial bet's first position upto the chain tip and calculate bet's costbasises and profits, breaks if rekt detected +int32_t prices_scanchain(std::vector &bets, int16_t leverage, std::vector vec, int64_t &lastprice, int32_t &endheight) { + + if (bets.size() == 0) + return -1; + + bool stop = false; + for (int32_t height = bets[0].firstheight+1; ; height++) // the last datum for 24h is the costbasis value { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) - { - addedbets = prices_batontxid(batontxid,bettx,bettxid); - mtx.vin.push_back(CTxIn(bettxid,1,CScript())); - for (i=0; i bets[i].firstheight) { + + int32_t retcode = prices_syntheticprofits(bets[i].costbasis, bets[i].firstheight, height, leverage, vec, bets[i].positionsize, bets[i].profits, lastprice); + if (retcode < 0) { + std::cerr << "prices_scanchain() prices_syntheticprofits returned -1, finishing..." << std::endl; + stop = true; break; } + totalposition += bets[i].positionsize; + totalprofits += bets[i].profits; } - if ( i == PRICES_DAYWINDOW ) - result.push_back(Pair("rekt",0)); - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - myfee = bettx.vout[1].nValue / 10; - result.push_back(Pair("myfee",myfee)); - mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[1].nValue-myfee-txfee,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_costbasisopret(bettxid,mypk,firstheight+PRICES_DAYWINDOW-1,costbasis)); - return(prices_rawtxresult(result,rawtx,0)); + } + + if (stop) + break; + + endheight = height; + int64_t equity = totalposition + totalprofits; + if (equity <= (int64_t)((double)totalposition * prices_minmarginpercent(leverage))) + { // we are in loss + break; } } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); + + return 0; +} + +// pricescostbasis rpc impl: set cost basis (open price) for the bet (deprecated) +UniValue PricesSetcostbasis(int64_t txfee, uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); + UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; CTransaction bettx; uint256 hashBlock, batontxid, tokenid; + int64_t myfee, positionsize = 0, addedbets, firstprice = 0, lastprice, profits = 0, costbasis = 0, equity; + int32_t i, firstheight = 0, height, numvouts; int16_t leverage = 0; + std::vector vec; + CPubKey pk, mypk, pricespk; std::string rawtx; +/* + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) + txfee = PRICES_TXFEE; + + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp, 0); + if (myGetTransaction(bettxid, bettx, hashBlock) != 0 && (numvouts = bettx.vout.size()) > 3) + { + if (prices_betopretdecode(bettx.vout[numvouts - 1].scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) == 'B') + { + if (nextheight <= firstheight + PRICES_DAYWINDOW + 1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cannot calculate costbasis yet")); + return(result); + } + + addedbets = prices_enumaddedbets(batontxid, bettx, bettxid); + mtx.vin.push_back(CTxIn(bettxid, 1, CScript())); // spend vin1 with betamount + //for (i = 0; i < PRICES_DAYWINDOW + 1; i++) // the last datum for 24h is the actual costbasis value + //{ + int32_t retcode = prices_syntheticprofits(costbasis, firstheight, firstheight + PRICES_DAYWINDOW, leverage, vec, positionsize, addedbets, profits, lastprice); + if (retcode < 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cannot calculate costbasis error getting price")); + return(result); + } + equity = positionsize + addedbets + profits; + //if (equity < 0) + //{ // we are in loss + // result.push_back(Pair("rekt", (int64_t)1)); + // result.push_back(Pair("rektheight", (int64_t)firstheight + i)); + // break; + //} + //} + //if (i == PRICES_DAYWINDOW + 1) + // result.push_back(Pair("rekt", 0)); + + prices_betjson(result, profits, costbasis, positionsize, addedbets, leverage, firstheight, firstprice, lastprice, equity); + + if (AddNormalinputs(mtx, mypk, txfee, 4) >= txfee) + { + myfee = bettx.vout[1].nValue / 10; // fee for setting costbasis + result.push_back(Pair("myfee", myfee)); + + mtx.vout.push_back(CTxOut(myfee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, bettx.vout[1].nValue - myfee, pricespk)); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_costbasisopret(bettxid, mypk, firstheight + PRICES_DAYWINDOW , costbasis)); // -1 + return(prices_rawtxresult(result, rawtx, 0)); + } + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); + return(result); + } + } */ + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "deprecated")); return(result); } -UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) + +// pricesaddfunding rpc impl: add yet another bet +UniValue PricesRefillFund(int64_t amount) { int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid,batontxid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = PRICES_TXFEE; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CPubKey pricespk, mypk, pk; + std::string rawtx; + //char myaddr[64]; + + cp = CCinit(&C, EVAL_PRICES); + const int64_t txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + pricespk = GetUnspendable(cp, 0); + + if (AddNormalinputs(mtx, mypk, amount + txfee, 64) >= amount + txfee) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode, amount, pricespk)); // vout1 added amount + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, CScript()); + return(prices_rawtxresult(result, rawtx, 0)); + + } + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); + return(result); +} + + +int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) +{ + CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + + if (myGetTransaction(bettxid, bettx, hashBlock) && bettx.vout.size() > 3) { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + if (hashBlock.IsNull()) + return -2; + + + // TODO: forget old tx + //CBlockIndex *bi = komodo_getblockindex(hashBlock); + //if (bi && bi->GetHeight() < 5342) + // return -5; + + OneBetData bet1; + if (prices_betopretdecode(bettx.vout.back().scriptPubKey, betinfo.pk, bet1.firstheight, bet1.positionsize, betinfo.leverage, betinfo.firstprice, betinfo.vecparsed, betinfo.tokenid) == 'B') { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,rektheight,leverage,vec,positionsize,addedbets)) < 0 ) - { - myfee = (positionsize + addedbets) / 500; - } - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - if ( myfee != 0 ) - { - mtx.vin.push_back(CTxIn(bettxid,2,CScript())); - mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[2].nValue-myfee-txfee,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,rektheight,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); - return(prices_rawtxresult(result,rawtx,0)); - } + uint256 finaltxid; + int32_t vini; + int32_t finaltxheight; //, endheight; + //std::vector bets; + betinfo.txid = bettxid; + + if (CCgetspenttxid(finaltxid, vini, finaltxheight, bettxid, NVOUT_CCMARKER) == 0) + betinfo.isOpen = false; else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","position not rekt")); - return(result); + betinfo.isOpen = true; + + // override with real amount (TODO: check this) + //bet1.positionsize = bettx.vout[2].nValue; + + betinfo.bets.push_back(bet1); + + prices_enumaddedbets(batontxid, betinfo.bets, bettxid); + + if (!betinfo.isOpen) { + CTransaction finaltx; + uint256 hashBlock; + vscript_t vopret; + if (myGetTransaction(finaltxid, finaltx, hashBlock) && finaltx.vout.size() > 0 && PricesCheckOpret(finaltx, vopret) != 0) { + uint8_t funcId = prices_finalopretdecode(finaltx.vout.back().scriptPubKey, betinfo.txid, betinfo.pk, betinfo.lastheight, betinfo.averageCostbasis, betinfo.lastprice, betinfo.liquidationprice, betinfo.equity, betinfo.exitfee); + if (funcId == 0) + return -3; + + betinfo.isRekt = (funcId == 'R'); + + // return 0; + } + else + return -6; } + + + if (prices_scanchain(betinfo.bets, betinfo.leverage, betinfo.vecparsed, betinfo.lastprice, betinfo.lastheight) < 0) { + return -4; + } + + mpz_t mpzTotalPosition; + mpz_t mpzTotalprofits; + mpz_t mpzTotalcostbasis; + + mpz_init(mpzTotalPosition); + mpz_init(mpzTotalprofits); + mpz_init(mpzTotalcostbasis); + + int64_t totalposition = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + mpz_t mpzProduct; + mpz_t mpzProfits; + + mpz_init(mpzProduct); + mpz_init(mpzProfits); + + //totalprofits += b.profits; + //dcostbasis += b.amount * (double)b.costbasis; + // costbasis += b.amount * (b.costbasis / PRICES_POINTFACTOR); // prevent int64 overflow (but we have underflow for 1/BTC) + // std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + //std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + mpz_set_ui(mpzProduct, b.costbasis); + mpz_mul_ui(mpzProduct, mpzProduct, (uint64_t)b.positionsize); // b.costbasis * b.amount + mpz_add(mpzTotalcostbasis, mpzTotalcostbasis, mpzProduct); //averageCostbasis += b.costbasis * b.amount; + + mpz_add_ui(mpzTotalPosition, mpzTotalPosition, (uint64_t)b.positionsize); //totalposition += b.amount; + mpz_add(mpzTotalprofits, mpzTotalprofits, mpzProfits); //totalprofits += b.profits; + + totalposition += b.positionsize; + totalprofits += b.profits; + + mpz_clear(mpzProduct); + mpz_clear(mpzProfits); + } + + betinfo.equity = totalposition + totalprofits; + //int64_t averageCostbasis = 0; + + if (mpz_get_ui(mpzTotalPosition) != 0) { //prevent zero div + mpz_t mpzAverageCostbasis; + mpz_init(mpzAverageCostbasis); + + //averageCostbasis = totalcostbasis / totalposition; + mpz_mul_ui(mpzTotalcostbasis, mpzTotalcostbasis, SATOSHIDEN); // profits *= SATOSHIDEN normalization to prevent loss of significance while division + mpz_tdiv_q(mpzAverageCostbasis, mpzTotalcostbasis, mpzTotalPosition); + + mpz_tdiv_q_ui(mpzAverageCostbasis, mpzAverageCostbasis, SATOSHIDEN); // profits /= SATOSHIDEN de-normalization + + betinfo.averageCostbasis = mpz_get_ui(mpzAverageCostbasis); + mpz_clear(mpzAverageCostbasis); + } + + betinfo.liquidationprice = 0; + if (betinfo.leverage != 0) {// prevent zero div + betinfo.liquidationprice = betinfo.averageCostbasis - (betinfo.averageCostbasis * (1 - prices_minmarginpercent(betinfo.leverage))) / betinfo.leverage; + } + + if (!betinfo.isRekt) { // not set by check for final tx + + if (betinfo.equity > (int64_t)((double)totalposition * prices_minmarginpercent(betinfo.leverage))) + betinfo.isRekt = false; + else + { + betinfo.isRekt = true; + betinfo.exitfee = (int64_t)(((double)totalposition * prices_minmarginpercent(betinfo.leverage)) / 10); // was: totalposition / 500 + } + } + + mpz_clear(mpzTotalPosition); + mpz_clear(mpzTotalprofits); + mpz_clear(mpzTotalcostbasis); + return 0; } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant decode opret")); - return(result); - } + return -3; } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); - return(result); + return (-1); } -UniValue PricesCashout(uint64_t txfee,uint256 bettxid) +// pricesrekt rpc: anyone can rekt a bet at some block where losses reached limit, collecting fee +UniValue PricesRekt(int64_t txfee, uint256 bettxid, int32_t rektheight) { int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,inputsum,ignore,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CTransaction bettx; + int64_t myfee = 0; + CPubKey pk, mypk, pricespk; + std::string rawtx; + char destaddr[64]; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) // TODO: what did we want to do with txfee in prices? txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,destaddr,pricespk); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) - { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + pricespk = GetUnspendable(cp, 0); + GetCCaddress(cp, destaddr, pricespk); + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,nextheight-1,leverage,vec,positionsize,addedbets)) < 0 ) + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + return(result); + } + + int64_t totalposition = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + totalposition += b.positionsize; + totalprofits += b.profits; + } + + + if (!betinfo.isOpen) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position closed")); + return result; + } + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); // fill output + if (betinfo.isRekt) + { + myfee = betinfo.exitfee; // consolation fee for loss + } + if (myfee != 0) + { + int64_t CCchange = 0, inputsum; + + mtx.vin.push_back(CTxIn(bettxid, NVOUT_CCMARKER, CScript())); // spend cc marker + if ((inputsum = AddPricesInputs(cp, mtx, destaddr, myfee + txfee, 64)) > myfee + txfee) // TODO: why do we take txfee from global addr and not from user's addr? + CCchange = (inputsum - myfee); + mtx.vout.push_back(CTxOut(myfee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if (CCchange >= txfee) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CCchange, pricespk)); + + /// mtx.vout.push_back(MakeCC1vout(cp->evalcode, bettx.vout[2].nValue - myfee - txfee, pricespk)); // change + + // make some PoW to get txid=0x00.....00 to 'faucet' rekts + fprintf(stderr, "start PoW at %u\n", (uint32_t)time(NULL)); + uint32_t nonce = rand() & 0xfffffff; + for (int i = 0; i<1000000; i++, nonce++) + { + CMutableTransaction tmpmtx = mtx; + //int32_t len; + //uint8_t txbuf[32768]; + + rawtx = FinalizeCCTx(0, cp, tmpmtx, mypk, txfee, prices_finalopret(true, bettxid, mypk, betinfo.lastheight, betinfo.averageCostbasis, betinfo.lastprice, betinfo.liquidationprice, betinfo.equity, myfee, nonce)); + //if ((len = (int32_t)rawtx.size()) > 0 && len < sizeof(txbuf) / sizeof(txbuf[0]) * 2) + //{ + // len >>= 1; // sizeof hex divide by 2 + //decode_hex(txbuf, len, (char *)rawtx.c_str()); + //bits256 hash = bits256_doublesha256(0, txbuf, len); + uint256 hash = tmpmtx.GetHash(); + //if ((hash.bytes[0] & 0xff) == 0 && (hash.bytes[31] & 0xff) == 0) + if ((hash.begin()[0] & 0xff) == 0 && (hash.begin()[31] & 0xff) == 0) + { + fprintf(stderr, "found valid txid after %d iterations %u\n", i, (uint32_t)time(NULL)); + return(prices_rawtxresult(result, rawtx, 0)); + } + //fprintf(stderr,"%02x%02x ",hash.bytes[0],hash.bytes[31]); + //} + } + fprintf(stderr, "couldnt generate valid txid %u\n", (uint32_t)time(NULL)); + + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "could not generate valid txid")); + return(result); + } + else + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position not rekt")); + return(result); + } +} + +// pricescashout rpc impl: bettor can cashout hit bet if it is not rekt +UniValue PricesCashout(int64_t txfee, uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); + UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; char destaddr[64]; + int64_t CCchange = 0, inputsum; + CPubKey pk, mypk, pricespk; + std::string rawtx; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) + txfee = PRICES_TXFEE; + + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp, 0); + GetCCaddress(cp, destaddr, pricespk); + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + return(result); + } + + int64_t totalposition = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + totalposition += b.positionsize; + totalprofits += b.profits; + } + + + if (!betinfo.isOpen) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position closed")); + return result; + } + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); // fill output json + + if (betinfo.isRekt) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position rekt")); + return(result); + } + + mtx.vin.push_back(CTxIn(bettxid, NVOUT_CCMARKER, CScript())); // spend cc marker + if ((inputsum = AddPricesInputs(cp, mtx, destaddr, betinfo.equity + txfee, 64)) > betinfo.equity + txfee) // TODO: why txfee from the fund? + CCchange = (inputsum - betinfo.equity); + mtx.vout.push_back(CTxOut(betinfo.equity, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if (CCchange >= txfee) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CCchange, pricespk)); + // TODO: what should the opret param be: + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_finalopret(false, bettxid, mypk, nextheight-1, betinfo.averageCostbasis, betinfo.lastprice, betinfo.liquidationprice, betinfo.equity, txfee, 0)); + return(prices_rawtxresult(result, rawtx, 0)); + +} + + +// pricesinfo rpc impl +UniValue PricesInfo(uint256 bettxid, int32_t refheight) +{ + UniValue result(UniValue::VOBJ); +/* CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + int64_t positionsize = 0, firstprice = 0, lastprice = 0; + int32_t firstheight = 0, endheight; + int16_t leverage = 0; + std::vector vec; + CPubKey pk, mypk, pricespk; + std::string rawtx; */ + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if( retcode == -1 ) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + else { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", retcode)); + } + return(result); + } + + if (!betinfo.isRekt) + result.push_back(Pair("rekt", 0)); + else + { + result.push_back(Pair("rekt", (int64_t)1)); + result.push_back(Pair("rektfee", betinfo.exitfee)); + result.push_back(Pair("rektheight", betinfo.lastheight)); + } + + result.push_back(Pair("open", betinfo.isOpen ? 1 : 0 )); + + std::string expr = prices_getsourceexpression(betinfo.vecparsed); + result.push_back(Pair("expression", expr)); + result.push_back(Pair("reduced", prices_getreducedexpr(expr))); +// result.push_back(Pair("batontxid", batontxid.GetHex())); + result.push_back(Pair("costbasis", ValueFromAmount(betinfo.averageCostbasis))); +#ifdef TESTMODE + result.push_back(Pair("costbasis_test_period", 7)); +#endif + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); + + result.push_back(Pair("LiquidationPrice", ValueFromAmount(betinfo.liquidationprice))); + + return(result); +} + +// priceslist rpc impl +UniValue PricesList(uint32_t filter, CPubKey mypk) +{ + UniValue result(UniValue::VARR); + std::vector > addressIndex, addressIndexCC; + struct CCcontract_info *cp, C; + + cp = CCinit(&C, EVAL_PRICES); + //pricespk = GetUnspendable(cp, 0); + + // filters and outputs prices bet txid + auto AddBetToList = [&](uint256 txid) + { + int64_t amount, firstprice; + int32_t height; + int16_t leverage; + uint256 hashBlock, tokenid; + CPubKey pk, pricespk; + std::vector vec; + CTransaction vintx; + + if (GetTransaction(txid, vintx, hashBlock, false) != 0) + { + + // TODO: forget old tx + //CBlockIndex *bi = komodo_getblockindex(hashBlock); + //if (bi && bi->GetHeight() < 5342) + // return; + + bool bAppend = false; + if (vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout.back().scriptPubKey, pk, height, amount, leverage, firstprice, vec, tokenid) == 'B' && + (mypk == CPubKey() || mypk == pk)) // if only mypubkey to list { - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - result.push_back(Pair("result","error")); - result.push_back(Pair("error","position rekt")); - return(result); + if (filter == 0) + bAppend = true; + else { + int32_t vini; + int32_t height; + uint256 finaltxid; + + int32_t spent = CCgetspenttxid(finaltxid, vini, height, txid, NVOUT_CCMARKER); + if (filter == 1 && spent < 0 || // open positions + filter == 2 && spent == 0) // closed positions + bAppend = true; + } + if (bAppend) + result.push_back(txid.GetHex()); } - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - mtx.vin.push_back(CTxIn(bettxid,2,CScript())); - if ( (inputsum= AddPricesInputs(cp,mtx,destaddr,profits+txfee,64,bettxid,2)) > profits+txfee ) - CCchange = (inputsum - profits); - mtx.vout.push_back(CTxOut(bettx.vout[2].nValue + profits,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - if ( CCchange >= txfee ) - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,nextheight-1,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); - return(prices_rawtxresult(result,rawtx,0)); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant decode opret")); - return(result); + std::cerr << "PricesList() " << " bettxid=" << txid.GetHex() << " mypk=" << HexStr(mypk) << " opretpk=" << HexStr(pk) << " filter=" << filter << " bAppend=" << bAppend << std::endl; } + }; + + + SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) + { + if( it->first.index == NVOUT_NORMALMARKER ) + AddBetToList(it->first.txhash); } + + /* for future when switch to cc marker only + SetCCtxids(addressIndexCC, cp->unspendableCCaddr, true); // cc marker + for (std::vector >::const_iterator it = addressIndexCC.begin(); it != addressIndexCC.end(); it++) + { + priceslist(it, 1); + } + */ return(result); } -UniValue PricesInfo(uint256 bettxid,int32_t refheight) + +static bool prices_addbookentry(uint256 txid, std::vector &book) { - UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) - { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) - { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,refheight,leverage,vec,positionsize,addedbets)) < 0 ) - { - result.push_back(Pair("rekt",1)); - result.push_back(Pair("rektfee",(positionsize + addedbets) / 500)); - } else result.push_back(Pair("rekt",0)); - result.push_back(Pair("batontxid",batontxid.GetHex())); - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - result.push_back(Pair("height",(int64_t)refheight)); - return(result); - } + BetInfo betinfo; + if (prices_getbetinfo(txid, betinfo) == 0) { + book.push_back(betinfo); + return true; } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); - return(result); + return false; } -UniValue PricesList() -{ - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - SetCCtxids(addressIndex,cp->normaladdr,false); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - txid = it->first.txhash; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) - { - if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) - { - result.push_back(uint256_str(str,txid)); + +static bool prices_ispositionup(const std::vector &vecparsed, int16_t leverage) { + if (vecparsed.size() > 1 && vecparsed.size() <= 3) { + uint16_t opcode = vecparsed[0]; + + int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // filter index or weight = opcode & (2048-1) + + if ((opcode & KOMODO_PRICEMASK) == 0) { + char name[65]; + if (komodo_pricename(name, value)) { + std::string upperquote, bottomquote; + prices_splitpair(std::string(name), upperquote, bottomquote); + + uint16_t opcode1 = vecparsed[1]; + bool isInverted = ((opcode1 & KOMODO_PRICEMASK) == PRICES_INV); + + //std::cerr << "prices_ispositionup upperquote=" << upperquote << " bottomquote=" << bottomquote << " opcode1=" << opcode1 << " (opcode1 & KOMODO_PRICEMASK)=" << (opcode1 & KOMODO_PRICEMASK) << std::endl; + + if (upperquote == "BTC" || bottomquote == "BTC") { // it is relatively btc + if (upperquote == "BTC" && (leverage > 0 && !isInverted || leverage < 0 && isInverted) || + bottomquote == "BTC" && (leverage < 0 && !isInverted || leverage > 0 && isInverted)) { + std::cerr << "prices_ispositionup returns true for BTC for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; + return true; + } + else { + std::cerr << "prices_ispositionup returns false for BTC for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; + return false; + } + } + + if (upperquote == "USD" || bottomquote == "USD") { // it is relatively usd + if (upperquote == "USD" && (leverage > 0 && !isInverted || leverage < 0 && isInverted) || + bottomquote == "USD" && (leverage < 0 && !isInverted || leverage > 0 && isInverted)) { + std::cerr << "prices_ispositionup returns true for USD for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; + return true; + } + else { + std::cerr << "prices_ispositionup returns false for USD for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; + return false; + } + } } } } - return(result); + std::cerr << "prices_ispositionup returns false for expr=" << prices_getsourceexpression(vecparsed) << " lev=" << leverage << std::endl; + return false; +} + +static bool prices_isopposite(BetInfo p1, BetInfo p2) { + if (p1.vecparsed.size() <= 3 && p2.vecparsed.size() <= 3) { // simple synthetic exp + + uint16_t opcode1 = p1.vecparsed[0]; + uint16_t opcode2 = p2.vecparsed[0]; + + int32_t value1 = (opcode1 & (KOMODO_MAXPRICES - 1)); // index or weight + int32_t value2 = (opcode2 & (KOMODO_MAXPRICES - 1)); // index or weight + + if ( (opcode1 & KOMODO_PRICEMASK) == 0 && (opcode2 & KOMODO_PRICEMASK) == 0 ) { + char name1[65]; + char name2[65]; + if (komodo_pricename(name1, value1) && komodo_pricename(name2, value2)) { + + uint16_t opcode1 = p1.vecparsed[1]; + uint16_t opcode2 = p2.vecparsed[1]; + + std::string upperquote1, bottomquote1, upperquote2, bottomquote2; + prices_splitpair(std::string(name1), upperquote1, bottomquote1); + prices_splitpair(std::string(name2), upperquote2, bottomquote2); + + bool isInverted1 = ((opcode1 & KOMODO_PRICEMASK) != PRICES_INV); + bool isInverted2 = ((opcode2 & KOMODO_PRICEMASK) != PRICES_INV); + + if (/*upperquote1 == bottomquote2 && bottomquote1 == upperquote2 && (p1.leverage > 0 == p2.leverage > 0 || (opcode1 & KOMODO_PRICEMASK) == PRICES_INV == (opcode2 & KOMODO_PRICEMASK) == PRICES_INV) ||*/ + upperquote1 == upperquote2 && bottomquote1 == bottomquote2 && ((p1.leverage > 0) != (p2.leverage > 0) || isInverted1 != isInverted2) ) + return true; + } + } + } + return false; } + +static std::string findMatchedBook(const std::vector &vecparsed, const std::map > & bookmatched) { + + if (vecparsed.size() > 1 && vecparsed.size() <= 3) { + uint16_t opcode = vecparsed[0]; + + int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // filter index or weight = opcode & (2048-1) + + if ((opcode & KOMODO_PRICEMASK) == 0) { + char name[65]; + if (komodo_pricename(name, value)) { + auto it = bookmatched.find(std::string(name)); + if (it != bookmatched.end()) + return it->first; + } + } + } + return std::string(""); +} + + +void prices_getorderbook(std::map > & bookmatched, std::map &matchedTotals, TotalFund &fundTotals) +{ + std::vector book; + std::vector > addressIndex; + struct CCcontract_info *cp, C; + + cp = CCinit(&C, EVAL_PRICES); + + // add all bets: + SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) + { + if (it->first.index == NVOUT_NORMALMARKER) + prices_addbookentry(it->first.txhash, book); + } + + + // calc total fund amount + fundTotals.totalFund = 0; + fundTotals.totalRekt = 0; + fundTotals.totalEquity = 0; + fundTotals.totalActiveBets = 0; + + std::vector > addressCCunspents; + SetCCunspents(addressCCunspents, cp->unspendableCCaddr, true); // cc marker + for (std::vector >::const_iterator it = addressCCunspents.begin(); it != addressCCunspents.end(); it++) + { + //std::cerr << "totalfund calc txid=" << it->first.txhash.GetHex() << " nvout=" << it->first.index << " satoshis=" << it->second.satoshis << std::endl; + fundTotals.totalFund += it->second.satoshis; + } + + // extract out opposite bets: + while (book.size() > 0) { + + int64_t totalPos = 0; + for (auto bet : book[0].bets) totalPos += bet.positionsize; + + if (book[0].isOpen) { + + fundTotals.totalActiveBets += totalPos; + fundTotals.totalEquity += book[0].equity; + + if (book[0].vecparsed.size() <= 3) { // only short expr check for match: "BTC_USD,1" or "BTC_USD,!,1" + char name[65]; + komodo_pricename(name, (book[0].vecparsed[0] & (KOMODO_MAXPRICES - 1))); + std::string sname = name; + bookmatched[sname].push_back(book[0]); + + for (int j = 1; j < book.size(); j++) { + if (book[0].isOpen && book[j].isOpen) { + if (prices_isopposite(book[0], book[j])) { + + bookmatched[sname].push_back(book[j]); + book.erase(book.begin() + j); + } + } + } + } + else { + // store as is + std::string sname = prices_getsourceexpression(book[0].vecparsed); + bookmatched[sname].push_back(book[0]); + } + } + else { + if( book[0].isRekt ) + fundTotals.totalRekt += (totalPos - book[0].exitfee); + else + fundTotals.totalCashout += (totalPos - book[0].exitfee); + + //TODO: store rekt + } + book.erase(book.begin()); + } + + // calculate cancelling amount + for (auto &m : bookmatched) { // note: use reference &m otherwise isUp will be changed only in a copy + int64_t totalLeveragedPositionUp = 0; + int64_t totalLeveragedPositionDown = 0; + + for (int i = 0; i < m.second.size(); i++) { + int64_t totalPos = 0; + for (auto bet : m.second[i].bets) totalPos += bet.positionsize; + m.second[i].isUp = prices_ispositionup(m.second[i].vecparsed, m.second[i].leverage); + if (m.second[i].isUp) + totalLeveragedPositionUp += totalPos * abs(m.second[i].leverage); + else + totalLeveragedPositionDown += totalPos * abs(m.second[i].leverage); + //std::cerr << "PricesGetOrderbook 0 m.second[i].isUp=" << m.second[i].isUp << " i=" << i << std::endl; + + } + matchedTotals[m.first].diffLeveragedPosition = totalLeveragedPositionUp - totalLeveragedPositionDown; + } +} + +static bool prices_isacceptableamount(const std::vector &vecparsed, int64_t amount, int16_t leverage) { + + std::map > matchedBook; + std::map matchedTotals; + TotalFund fundTotals; + + prices_getorderbook(matchedBook, matchedTotals, fundTotals); + std::string pricename = findMatchedBook(vecparsed, matchedBook); + if (!pricename.empty()) { + std::cerr << "prices_isacceptableamount() found matched book=" << pricename << " diffLeveragedPosition=" << matchedTotals[pricename].diffLeveragedPosition << " expr=" << prices_getsourceexpression(vecparsed) << std::endl; + // could fit into leveraged amount + if (prices_ispositionup(vecparsed, leverage) && amount*abs(leverage) + matchedTotals[pricename].diffLeveragedPosition <= 0) { + std::cerr << "prices_isacceptableamount() could fit into opposite negative lev amount" << std::endl; + return true; + } + if (!prices_ispositionup(vecparsed, leverage) && -amount*abs(leverage) + matchedTotals[pricename].diffLeveragedPosition >= 0) { + std::cerr << "prices_isacceptableamount() could fit into opposite positive lev amount" << std::endl; + return true; + } + } + + std::cerr << "prices_isacceptableamount() amount=" << amount << " leverage=" << leverage << " fundTotals.totalFund=" << fundTotals.totalFund << " fundTotals.totalEquity=" << fundTotals.totalEquity << std::endl; + // if not fit to matched = allow to bet for leveraged amount no more than 10% from free fund + if (amount * leverage < (fundTotals.totalFund - fundTotals.totalEquity) * PRICES_MINAVAILFUNDFRACTION) + return true; + + return false; +} + + +// walk through uxtos on the global address +// calculate the balance: +// + rekt positions +// = opposite positions +// - unbalanced positions +UniValue PricesGetOrderbook() +{ + UniValue result(UniValue::VOBJ); + std::map > matchedBook; + std::map matchedTotals; + TotalFund fundTotals; + + prices_getorderbook(matchedBook, matchedTotals, fundTotals); + + /*UniValue resbook (UniValue::VARR); + for (int i = 0; i < book.size(); i++) { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("expression", prices_getsourceexpression(book[i].vecparsed))); + entry.push_back(Pair("costbasis", book[i].averageCostbasis)); + entry.push_back(Pair("leverage", book[i].leverage)); + entry.push_back(Pair("equity", book[i].equity)); + resbook.push_back(entry); + } + result.push_back(Pair("unmatched", resbook)); */ + + // copy to rpc UniResult + for (auto &m : matchedBook) { + UniValue resheader(UniValue::VOBJ); + UniValue resbook(UniValue::VARR); + for (int i = 0; i < m.second.size(); i++) { + UniValue entry(UniValue::VOBJ); + + int64_t totalPos = 0; + for (auto bet : m.second[i].bets) totalPos += bet.positionsize; + entry.push_back(Pair("isOpen", (m.second[i].isOpen ? 1 : 0 ))); + entry.push_back(Pair("expression", prices_getsourceexpression(m.second[i].vecparsed))); + entry.push_back(Pair("positionsize", totalPos)); + entry.push_back(Pair("leverage", m.second[i].leverage)); + entry.push_back(Pair("costbasis", m.second[i].averageCostbasis)); + entry.push_back(Pair("lastprice", m.second[i].lastprice)); + entry.push_back(Pair("equity", m.second[i].equity)); + entry.push_back(Pair("isUpPosition", (m.second[i].isUp ? 1 : 0))); + resbook.push_back(entry); + } + resheader.push_back(Pair("positions", resbook)); + resheader.push_back(Pair("DiffLeveragedPosition", matchedTotals[m.first].diffLeveragedPosition)); + result.push_back(Pair(m.first, resheader)); + } + + //int64_t totalLiabilities = 0; + /* empty + for (int i = 0; i < book.size(); i++) { + if (book[i].isOpen) { + int64_t t = 0; + for (auto b : book[i].bets) t += b.positionsize; + std::cerr << "book[i].txid=" << book[i].txid.GetHex() << " exp=" << prices_getsourceexpression(book[i].vecparsed) << " totalpos=" << t << " equity=" << book[i].equity << std::endl; + totalLiabilities += book[i].equity; + } + } */ + + result.push_back(Pair("TotalFund", ValueFromAmount(fundTotals.totalFund))); + result.push_back(Pair("TotalEquity", ValueFromAmount(fundTotals.totalEquity))); + result.push_back(Pair("TotalRekt", ValueFromAmount(fundTotals.totalRekt))); + result.push_back(Pair("TotalBets", ValueFromAmount(fundTotals.totalActiveBets))); + result.push_back(Pair("TotalCashoutBets", ValueFromAmount(fundTotals.totalCashout))); + +// result.push_back(Pair("TotalLiabilities", ValueFromAmount(totalLiabilities))); + return result; +} diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index 5ca2b038a..2edd275ec 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -195,6 +195,7 @@ bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransactio bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { uint256 txid,fundingtxid,hashBlock,vinfundingtxid; uint64_t vinsbits,sbits,APR,minseconds,maxseconds,mindeposit,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx; + int64_t dummy; numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; @@ -255,7 +256,7 @@ bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &t if ( (*cp->ismyvin)(tx.vin[i].scriptSig) == 0 ) return eval->Invalid("unexpected normal vin for unlock"); } - if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime) ) + if ( !CheckTxFee(tx, txfee, chainActive.LastTip()->GetHeight(), chainActive.LastTip()->nTime, dummy) ) return eval->Invalid("txfee is too high"); amount = vinTx.vout[0].nValue; reward = RewardsCalc(amount,tx.vin[0].prevout.hash,APR,minseconds,maxseconds,mindeposit); diff --git a/src/chain.h b/src/chain.h index d810ed4cb..b893e3a06 100644 --- a/src/chain.h +++ b/src/chain.h @@ -28,6 +28,7 @@ class CChainPower; #include "pow.h" #include "tinyformat.h" #include "uint256.h" +extern int8_t is_STAKED(const char *chain_name); #include @@ -36,6 +37,8 @@ class CChainPower; static const int SPROUT_VALUE_VERSION = 1001400; static const int SAPLING_VALUE_VERSION = 1010100; extern int32_t ASSETCHAINS_LWMAPOS; +extern char ASSETCHAINS_SYMBOL[65]; +extern uint64_t ASSETCHAINS_NOTARY_PAY[]; struct CDiskBlockPos { @@ -118,7 +121,7 @@ enum BlockStatus: uint32_t { BLOCK_FAILED_MASK = BLOCK_FAILED_VALID | BLOCK_FAILED_CHILD, BLOCK_ACTIVATES_UPGRADE = 128, //! block activates a network upgrade - BLOCK_IN_TMPFILE = 256 + BLOCK_IN_TMPFILE = 256 }; //! Short-hand for the highest consensus validity we implement. @@ -238,7 +241,7 @@ public: CBlockIndex* pskip; //! height of the entry in the chain. The genesis block has height 0 - int64_t newcoins,zfunds,sproutfunds; int8_t segid; // jl777 fields + int64_t newcoins,zfunds,sproutfunds,nNotaryPay; int8_t segid; // jl777 fields //! Which # file this block is stored in (blk?????.dat) int nFile; @@ -309,6 +312,7 @@ public: phashBlock = NULL; newcoins = zfunds = 0; segid = -2; + nNotaryPay = 0; pprev = NULL; pskip = NULL; nFile = 0; @@ -543,6 +547,11 @@ public: if ((s.GetType() & SER_DISK) && (nVersion >= SAPLING_VALUE_VERSION)) { READWRITE(nSaplingValue); } + if ( (s.GetType() & SER_DISK) && (is_STAKED(ASSETCHAINS_SYMBOL) != 0) && ASSETCHAINS_NOTARY_PAY[0] != 0 ) + { + READWRITE(nNotaryPay); + READWRITE(segid); + } } uint256 GetBlockHash() const diff --git a/src/crypto/verus_hash.h b/src/crypto/verus_hash.h index 2f1f2cd26..63ff1aaaa 100644 --- a/src/crypto/verus_hash.h +++ b/src/crypto/verus_hash.h @@ -1,134 +1,134 @@ -// (C) 2018 Michael Toutonghi -// Distributed under the MIT software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -/* -This provides the PoW hash function for Verus, enabling CPU mining. -*/ -#ifndef VERUS_HASH_H_ -#define VERUS_HASH_H_ - -#include -#include - -#include - -extern "C" -{ -#include "crypto/haraka.h" -#include "crypto/haraka_portable.h" -} - -class CVerusHash -{ - public: - static void Hash(void *result, const void *data, size_t len); - static void (*haraka512Function)(unsigned char *out, const unsigned char *in); - - static void init(); - - CVerusHash() { } - - CVerusHash &Write(const unsigned char *data, size_t len); - - CVerusHash &Reset() - { - curBuf = buf1; - result = buf2; - curPos = 0; - std::fill(buf1, buf1 + sizeof(buf1), 0); - return *this; - } - - int64_t *ExtraI64Ptr() { return (int64_t *)(curBuf + 32); } - void ClearExtra() - { - if (curPos) - { - std::fill(curBuf + 32 + curPos, curBuf + 64, 0); - } - } - void ExtraHash(unsigned char hash[32]) { (*haraka512Function)(hash, curBuf); } - - void Finalize(unsigned char hash[32]) - { - if (curPos) - { - std::fill(curBuf + 32 + curPos, curBuf + 64, 0); - (*haraka512Function)(hash, curBuf); - } - else - std::memcpy(hash, curBuf, 32); - } - - private: - // only buf1, the first source, needs to be zero initialized - unsigned char buf1[64] = {0}, buf2[64]; - unsigned char *curBuf = buf1, *result = buf2; - size_t curPos = 0; -}; - -class CVerusHashV2 -{ - public: - static void Hash(void *result, const void *data, size_t len); - static void (*haraka512Function)(unsigned char *out, const unsigned char *in); - - static void init(); - - CVerusHashV2() {} - - CVerusHashV2 &Write(const unsigned char *data, size_t len); - - CVerusHashV2 &Reset() - { - curBuf = buf1; - result = buf2; - curPos = 0; - std::fill(buf1, buf1 + sizeof(buf1), 0); - return *this; - } - - int64_t *ExtraI64Ptr() { return (int64_t *)(curBuf + 32); } - void ClearExtra() - { - if (curPos) - { - std::fill(curBuf + 32 + curPos, curBuf + 64, 0); - } - } - void ExtraHash(unsigned char hash[32]) { (*haraka512Function)(hash, curBuf); } - - void Finalize(unsigned char hash[32]) - { - if (curPos) - { - std::fill(curBuf + 32 + curPos, curBuf + 64, 0); - (*haraka512Function)(hash, curBuf); - } - else - std::memcpy(hash, curBuf, 32); - } - - private: - // only buf1, the first source, needs to be zero initialized - unsigned char buf1[64] = {0}, buf2[64]; - unsigned char *curBuf = buf1, *result = buf2; - size_t curPos = 0; -}; - -extern void verus_hash(void *result, const void *data, size_t len); -extern void verus_hash_v2(void *result, const void *data, size_t len); - -inline bool IsCPUVerusOptimized() -{ - unsigned int eax,ebx,ecx,edx; - - if (!__get_cpuid(1,&eax,&ebx,&ecx,&edx)) - { - return false; - } - return ((ecx & (bit_AVX | bit_AES)) == (bit_AVX | bit_AES)); -}; - -#endif +// (C) 2018 Michael Toutonghi +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/* +This provides the PoW hash function for Verus, enabling CPU mining. +*/ +#ifndef VERUS_HASH_H_ +#define VERUS_HASH_H_ + +#include +#include + +#include + +extern "C" +{ +#include "crypto/haraka.h" +#include "crypto/haraka_portable.h" +} + +class CVerusHash +{ + public: + static void Hash(void *result, const void *data, size_t len); + static void (*haraka512Function)(unsigned char *out, const unsigned char *in); + + static void init(); + + CVerusHash() { } + + CVerusHash &Write(const unsigned char *data, size_t len); + + CVerusHash &Reset() + { + curBuf = buf1; + result = buf2; + curPos = 0; + std::fill(buf1, buf1 + sizeof(buf1), 0); + return *this; + } + + int64_t *ExtraI64Ptr() { return (int64_t *)(curBuf + 32); } + void ClearExtra() + { + if (curPos) + { + std::fill(curBuf + 32 + curPos, curBuf + 64, 0); + } + } + void ExtraHash(unsigned char hash[32]) { (*haraka512Function)(hash, curBuf); } + + void Finalize(unsigned char hash[32]) + { + if (curPos) + { + std::fill(curBuf + 32 + curPos, curBuf + 64, 0); + (*haraka512Function)(hash, curBuf); + } + else + std::memcpy(hash, curBuf, 32); + } + + private: + // only buf1, the first source, needs to be zero initialized + unsigned char buf1[64] = {0}, buf2[64]; + unsigned char *curBuf = buf1, *result = buf2; + size_t curPos = 0; +}; + +class CVerusHashV2 +{ + public: + static void Hash(void *result, const void *data, size_t len); + static void (*haraka512Function)(unsigned char *out, const unsigned char *in); + + static void init(); + + CVerusHashV2() {} + + CVerusHashV2 &Write(const unsigned char *data, size_t len); + + CVerusHashV2 &Reset() + { + curBuf = buf1; + result = buf2; + curPos = 0; + std::fill(buf1, buf1 + sizeof(buf1), 0); + return *this; + } + + int64_t *ExtraI64Ptr() { return (int64_t *)(curBuf + 32); } + void ClearExtra() + { + if (curPos) + { + std::fill(curBuf + 32 + curPos, curBuf + 64, 0); + } + } + void ExtraHash(unsigned char hash[32]) { (*haraka512Function)(hash, curBuf); } + + void Finalize(unsigned char hash[32]) + { + if (curPos) + { + std::fill(curBuf + 32 + curPos, curBuf + 64, 0); + (*haraka512Function)(hash, curBuf); + } + else + std::memcpy(hash, curBuf, 32); + } + + private: + // only buf1, the first source, needs to be zero initialized + unsigned char buf1[64] = {0}, buf2[64]; + unsigned char *curBuf = buf1, *result = buf2; + size_t curPos = 0; +}; + +extern void verus_hash(void *result, const void *data, size_t len); +extern void verus_hash_v2(void *result, const void *data, size_t len); + +inline bool IsCPUVerusOptimized() +{ + unsigned int eax,ebx,ecx,edx; + + if (!__get_cpuid(1,&eax,&ebx,&ecx,&edx)) + { + return false; + } + return ((ecx & (bit_AVX | bit_AES)) == (bit_AVX | bit_AES)); +}; + +#endif diff --git a/src/deprecation.h b/src/deprecation.h index 427234b4a..dde45e22c 100644 --- a/src/deprecation.h +++ b/src/deprecation.h @@ -24,7 +24,7 @@ // * Shut down WEEKS_UNTIL_DEPRECATION weeks' worth of blocks after the estimated release block height. // * A warning is shown during the DEPRECATION_WARN_LIMIT worth of blocks prior to shut down. static const int WEEKS_UNTIL_DEPRECATION = 52; -static const int DEPRECATION_HEIGHT = 1600000; +static const int DEPRECATION_HEIGHT = 2200000; static const int APPROX_RELEASE_HEIGHT = DEPRECATION_HEIGHT - (WEEKS_UNTIL_DEPRECATION * 7 * 24 * 60); // Number of blocks before deprecation to warn users diff --git a/src/fiat/k64 b/src/fiat/k64 new file mode 100755 index 000000000..a3b3bc835 --- /dev/null +++ b/src/fiat/k64 @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=K64 $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/zex b/src/fiat/zex deleted file mode 100755 index f4573c093..000000000 --- a/src/fiat/zex +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/bash -./komodo-cli -ac_name=ZEX $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/zexo b/src/fiat/zexo new file mode 100755 index 000000000..b6fd508f2 --- /dev/null +++ b/src/fiat/zexo @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=ZEXO $1 $2 $3 $4 $5 $6 diff --git a/src/init.cpp b/src/init.cpp index 4003f9ced..107e3e476 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -35,6 +35,7 @@ #include "httprpc.h" #include "key.h" #include "notarisationdb.h" + #ifdef ENABLE_MINING #include "key_io.h" #endif @@ -55,6 +56,7 @@ #ifdef ENABLE_WALLET #include "wallet/wallet.h" #include "wallet/walletdb.h" + #endif #include #include @@ -89,9 +91,11 @@ using namespace std; extern void ThreadSendAlert(); +extern bool komodo_dailysnapshot(int32_t height); extern int32_t KOMODO_LOADINGBLOCKS; extern bool VERUS_MINTBLOCKS; extern char ASSETCHAINS_SYMBOL[]; +extern int32_t KOMODO_SNAPSHOT_INTERVAL; ZCJoinSplit* pzcashParams = NULL; @@ -1622,6 +1626,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) strLoadError = _("You need to rebuild the database using -reindex to go back to unpruned mode. This will redownload the entire blockchain"); break; } + + if ( ASSETCHAINS_CC != 0 && KOMODO_SNAPSHOT_INTERVAL != 0 && chainActive.Height() >= KOMODO_SNAPSHOT_INTERVAL ) + { + if ( !komodo_dailysnapshot(chainActive.Height()) ) + { + strLoadError = _("daily snapshot failed, please reindex your chain."); + break; + } + } if (!fReindex) { uiInterface.InitMessage(_("Rewinding blocks if needed...")); diff --git a/src/komodo.h b/src/komodo.h index b4c7d12bf..4de6ee4f3 100644 --- a/src/komodo.h +++ b/src/komodo.h @@ -713,9 +713,9 @@ int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notar sp->MoMdepth = MoMdepth; } komodo_stateupdate(height,0,0,0,zero,0,0,0,0,0,0,0,0,0,0,sp->MoM,sp->MoMdepth); - if ( ASSETCHAINS_SYMBOL[0] != 0 ) + //if ( ASSETCHAINS_SYMBOL[0] != 0 ) printf("[%s] ht.%d NOTARIZED.%d %s.%s %sTXID.%s lens.(%d %d) MoM.%s %d\n",ASSETCHAINS_SYMBOL,height,sp->NOTARIZED_HEIGHT,ASSETCHAINS_SYMBOL[0]==0?"KMD":ASSETCHAINS_SYMBOL,srchash.ToString().c_str(),ASSETCHAINS_SYMBOL[0]==0?"BTC":"KMD",desttxid.ToString().c_str(),opretlen,len,sp->MoM.ToString().c_str(),sp->MoMdepth); - + if ( ASSETCHAINS_SYMBOL[0] == 0 ) { if ( signedfp == 0 ) @@ -803,7 +803,10 @@ int32_t komodo_notarycmp(uint8_t *scriptPubKey,int32_t scriptlen,uint8_t pubkeys return(-1); } -// int32_t ! +// int32_t (!!!) +/* + read blackjok3rtt comments in main.cpp +*/ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) { static int32_t hwmheight; @@ -897,8 +900,8 @@ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) } numvalid = bitweight(signedmask); if ( ((height < 90000 || (signedmask & 1) != 0) && numvalid >= KOMODO_MINRATIFY) || - (numvalid >= KOMODO_MINRATIFY && ASSETCHAINS_SYMBOL[0] != 0) || - numvalid > (numnotaries/5) ) + (numvalid >= KOMODO_MINRATIFY && ASSETCHAINS_SYMBOL[0] != 0) || + numvalid > (numnotaries/5) ) { if ( !fJustCheck && ASSETCHAINS_SYMBOL[0] != 0) { @@ -918,7 +921,7 @@ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) fflush(signedfp); } transaction = i; - printf("[%s] ht.%d txi.%d signedmask.%llx numvins.%d numvouts.%d <<<<<<<<<<< notarized\n",ASSETCHAINS_SYMBOL,height,i,(long long)signedmask,numvins,numvouts); + printf("[%s] ht.%d txi.%d signedmask.%llx numvins.%d numvouts.%d <<<<<<<<<<< notarized\n",ASSETCHAINS_SYMBOL,height,i,(long long)signedmask,numvins,numvouts); } notarized = 1; } @@ -1014,7 +1017,9 @@ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) printf("%s ht.%d\n",ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL,height); if ( !fJustCheck && pindex->GetHeight() == hwmheight ) komodo_stateupdate(height,0,0,0,zero,0,0,0,0,height,(uint32_t)pindex->nTime,0,0,0,0,zero,0); - } else fprintf(stderr,"komodo_connectblock: unexpected null pindex\n"); + } + else + { fprintf(stderr,"komodo_connectblock: unexpected null pindex\n"); return(0); } //KOMODO_INITDONE = (uint32_t)time(NULL); //fprintf(stderr,"%s end connect.%d\n",ASSETCHAINS_SYMBOL,pindex->GetHeight()); if (fJustCheck) @@ -1025,6 +1030,12 @@ int32_t komodo_connectblock(bool fJustCheck, CBlockIndex *pindex,CBlock& block) return(1); if ( notarisations.size() > 1 || (notarisations.size() == 1 && notarisations[0] != 1) ) return(-1); + + fprintf(stderr,"komodo_connectblock: unxexpected behaviour when fJustCheck == true, report blackjok3rtt plz ! \n"); + /* this needed by gcc-8, it counts here that control reaches end of non-void function without this. + by default, we count that if control reached here -> the valid notarization isnt in position 1 or there are too many notarizations in this block. + */ + return(-1); } else return(0); } diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 80460b706..48962b015 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -29,6 +29,7 @@ int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notar unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); bool EnsureWalletIsAvailable(bool avoidException); extern bool fRequestShutdown; +extern CScript KOMODO_EARLYTXID_SCRIPTPUB; int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); @@ -681,11 +682,32 @@ int32_t komodo_WhoStaked(CBlock *pblock, CTxDestination &addressout) bool MarmaraPoScheck(char *destaddr,CScript opret,CTransaction staketx); -int32_t komodo_isPoS(CBlock *pblock,int32_t height) +int32_t komodo_isPoS2(CBlock *pblock) { - int32_t n,vout,numvouts; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; CScript opret; + CBlockIndex *pindex = komodo_blockindex(pblock->GetHash()); + if ( pindex != 0 && pindex->segid >= -1 ) + { + //fprintf(stderr,"isPoSblock segid.%d\n",pindex->segid); + if ( pindex->segid == -1 ) + return(0); + else return(1); + } + return (-1); +} + +int32_t komodo_isPoS(CBlock *pblock,int32_t height,bool fJustCheck) +{ + int32_t n,vout,numvouts,ret; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid; CScript opret; if ( ASSETCHAINS_STAKED != 0 ) { + if ( fJustCheck ) + { + // check pindex first, if that does not work, continue with slow check. + if ( (ret= komodo_isPoS2(pblock)) == 1 ) + return (1); + else if ( ret == 0 ) + return (0); + } n = pblock->vtx.size(); //fprintf(stderr,"ht.%d check for PoS numtx.%d numvins.%d numvouts.%d\n",height,n,(int32_t)pblock->vtx[n-1].vin.size(),(int32_t)pblock->vtx[n-1].vout.size()); if ( n > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1+(ASSETCHAINS_MARMARA!=0) ) @@ -1400,10 +1422,6 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he if ( ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASH || ASSETCHAINS_ALGO == ASSETCHAINS_VERUSHASHV1_1 ) percPoS = (percPoS*100) / (m+n); else - // This seems to be inverse. The actual PoS % is backwards in the first 100 blocks. - // I dont't understand the math here, or why its backwards, so I am just disabling it for VerusHash. - // No doubt this is probably wrong for equihash aswell, we may need to test an equihash chain with the rule above. - // Need to ask james what the deal is here! Seems to be causeing ALL the problems. percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100; } if ( dispflag != 0 && ASSETCHAINS_STAKED < 100 ) @@ -1584,7 +1602,7 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_uint256 bnTarget,arith_uint256 bhash) { - CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress; arith_uint256 POWTarget; + CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t ret,vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress; arith_uint256 POWTarget; if ( ASSETCHAINS_STAKED == 100 && height <= 10 ) return(1); BlockMap::const_iterator it = mapBlockIndex.find(pblock->GetHash()); @@ -1611,7 +1629,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ vout = pblock->vtx[txn_count-1].vin[0].prevout.n; if ( slowflag != 0 && prevtime != 0 ) { - if ( komodo_isPoS(pblock,height) != 0 ) + if ( komodo_isPoS(pblock,height,false) != 0 ) { eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+27,(char *)"",PoSperc); } @@ -1643,7 +1661,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ } else if ( slowflag == 0 ) // previous blocks are not seen yet, do the best approx { - if ( komodo_isPoS(pblock,height) != 0 ) + if ( komodo_isPoS(pblock,height,false) != 0 ) isPoS = 1; } if ( slowflag != 0 && isPoS != 0 ) @@ -2045,9 +2063,77 @@ uint64_t komodo_checknotarypay(CBlock *pblock,int32_t height) return(0); } +bool komodo_appendACscriptpub() +{ + static bool didinit = false; + if ( didinit ) + return didinit; + if ( ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()] == 49 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()-1] == 51 ) + { + CTransaction tx; uint256 blockhash; + // get transaction and check that it occured before height 100. + if ( myGetTransaction(KOMODO_EARLYTXID,tx,blockhash) && mapBlockIndex[blockhash]->GetHeight() < 100 ) + { + for (int i = 0; i < tx.vout.size(); i++) + { + if ( tx.vout[i].scriptPubKey[0] == OP_RETURN ) + { + ASSETCHAINS_SCRIPTPUB.pop_back(); ASSETCHAINS_SCRIPTPUB.pop_back(); // remove last 2 chars. + // get OP_RETURN from txid and append the HexStr of it to scriptpub + ASSETCHAINS_SCRIPTPUB.append(HexStr(tx.vout[i].scriptPubKey.begin()+3, tx.vout[i].scriptPubKey.end())); + //fprintf(stderr, "ac_script.%s\n",ASSETCHAINS_SCRIPTPUB.c_str()); + didinit = true; + return true; + } + } + } + fprintf(stderr, "could not get KOMODO_EARLYTXID.%s OP_RETURN data. Restart with correct txid!\n", KOMODO_EARLYTXID.GetHex().c_str()); + StartShutdown(); + } + return false; +} + +void GetKomodoEarlytxidScriptPub() +{ + if ( KOMODO_EARLYTXID == zeroid ) + { + fprintf(stderr, "Restart deamon with -earlytxid.\n"); + StartShutdown(); + return; + } + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_SNAPSHOT_INTERVAL == 0 ) + { + fprintf(stderr, "Prices->paymentsCC contract must have -ac_snapshot enabled to pay out.\n"); + StartShutdown(); + return; + } + if ( chainActive.Height() < 100 ) + { + fprintf(stderr, "Cannot fetch -earlytxid before block 100.\n"); + StartShutdown(); + return; + } + CTransaction tx; uint256 blockhash; int32_t i; + // get transaction and check that it occured before height 100. + if ( myGetTransaction(KOMODO_EARLYTXID,tx,blockhash) && mapBlockIndex[blockhash]->GetHeight() < 100 ) + { + for (i = 0; i < tx.vout.size(); i++) + if ( tx.vout[i].scriptPubKey[0] == OP_RETURN ) + break; + if ( i < tx.vout.size() ) + { + KOMODO_EARLYTXID_SCRIPTPUB = CScript(tx.vout[i].scriptPubKey.begin()+3, tx.vout[i].scriptPubKey.end()); + fprintf(stderr, "KOMODO_EARLYTXID_SCRIPTPUB.%s\n", HexStr(KOMODO_EARLYTXID_SCRIPTPUB.begin(),KOMODO_EARLYTXID_SCRIPTPUB.end()).c_str()); + return; + } + } + fprintf(stderr, "INVALID -earlytxid, restart daemon with correct txid.\n"); + StartShutdown(); +} + int64_t komodo_checkcommission(CBlock *pblock,int32_t height) { - int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0; + int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0; static bool didinit = false; if ( ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 ) { checktoshis = komodo_commission(pblock,height); @@ -2069,6 +2155,12 @@ int64_t komodo_checkcommission(CBlock *pblock,int32_t height) } if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { + static bool didinit = false; + if ( !didinit && height > 100 && KOMODO_EARLYTXID != zeroid && komodo_appendACscriptpub() ) + { + fprintf(stderr, "appended CC_op_return to ASSETCHAINS_SCRIPTPUB.%s\n", ASSETCHAINS_SCRIPTPUB.c_str()); + didinit = true; + } if ( ASSETCHAINS_SCRIPTPUB.size()/2 == scriptlen && scriptlen < sizeof(scripthex) ) { decode_hex(scripthex,scriptlen,(char *)ASSETCHAINS_SCRIPTPUB.c_str()); @@ -2205,11 +2297,11 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) else if ( ASSETCHAINS_STAKED != 0 ) failed = 0; } - if ( failed == 0 && ASSETCHAINS_COMMISSION != 0 ) //ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 ) + if ( failed == 0 && ASSETCHAINS_COMMISSION != 0 ) { if ( height == 1 ) { - if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) + if ( ASSETCHAINS_SCRIPTPUB.size() > 1 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()] != 49 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()-1] != 51 ) { int32_t scriptlen; uint8_t scripthex[10000]; script = (uint8_t *)&pblock->vtx[0].vout[0].scriptPubKey[0]; @@ -2221,7 +2313,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) return(-1); } else return(-1); } - else + else if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 ) { script = (uint8_t *)&pblock->vtx[0].vout[0].scriptPubKey[0]; scriptlen = (int32_t)pblock->vtx[0].vout[0].scriptPubKey.size(); @@ -2428,7 +2520,6 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt komodo_segids(hashbuf,nHeight-101,100); if ( *blocktimep < tipindex->nTime+60) *blocktimep = tipindex->nTime+60; -//fprintf(stderr,"Start scan of utxo for staking %u ht.%d\n",(uint32_t)time(NULL),nHeight); bool resetstaker = false; if ( array != 0 ) @@ -2513,9 +2604,8 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt } } lasttime = (uint32_t)time(NULL); -//fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); + //fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); } -//fprintf(stderr,"numkp.%d blocktime.%u\n",numkp,*blocktimep); block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + 57; for (i=winners=0; itxid,kp->vout,0,(uint32_t)tipindex->nTime+27,kp->address,PoSperc); - //fprintf(stderr,"i.%d %u vs %u\n",i,eligible2,eligible); if ( eligible > 0 ) { - besttime = m = 0; + besttime = 0; if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address,PoSperc) ) { - while ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address,PoSperc) ) + // have elegible utxo to stake with. + if ( earliest == 0 || eligible < earliest || (eligible == earliest && (*utxovaluep == 0 || kp->nValue < *utxovaluep)) ) { - besttime = eligible; - eligible--; - if ( eligible < block_from_future_rejecttime ) // nothing gained by going earlier - break; - m++; -//fprintf(stderr,"m.%d ht.%d validated winning blocktime %u -> %.8f eligible.%u test prior\n",m,nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); + // is better than the previous best, so use it instead. + earliest = eligible; + best_scriptPubKey = kp->scriptPubKey; + *utxovaluep = (uint64_t)kp->nValue; + decode_hex((uint8_t *)utxotxidp,32,(char *)kp->txid.GetHex().c_str()); + *utxovoutp = kp->vout; + *txtimep = kp->txtime; } - } - else - { - //fprintf(stderr,"ht.%d error validating winning blocktime %u -> %.8f eligible.%u test prior\n",nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); - continue; - } - eligible = besttime; - winners++; -//fprintf(stderr,"ht.%d validated winning [%d] -> %.8f eligible.%u test prior\n",nHeight,(int32_t)(eligible - tipindex->nTime),(double)kp->nValue/COIN,eligible); - if ( earliest == 0 || eligible < earliest || (eligible == earliest && (*utxovaluep == 0 || kp->nValue < *utxovaluep)) ) - { - earliest = eligible; - best_scriptPubKey = kp->scriptPubKey; //out.tx->vout[out.i].scriptPubKey; - *utxovaluep = (uint64_t)kp->nValue; - //decode_hex((uint8_t *)utxotxidp,32,(char *)out.tx->GetHash().GetHex().c_str()); - decode_hex((uint8_t *)utxotxidp,32,(char *)kp->txid.GetHex().c_str()); - *utxovoutp = kp->vout; - *txtimep = kp->txtime;//(uint32_t)out.tx->nLockTime; - //fprintf(stderr,"ht.%d earliest.%u [%d].%d (%s) nValue %.8f locktime.%u counter.%d winners.%d\n",nHeight,earliest,(int32_t)(earliest - tipindex->nTime),m,kp->address,(double)kp->nValue/COIN,*txtimep,counter,winners); - } - } //else fprintf(stderr,"utxo not eligible\n"); + if ( eligible < block_from_future_rejecttime ) // nothing gained by going earlier + break; + } else continue; + } } - if ( numkp < 1000 && array != 0 ) + if ( numkp < 500 && array != 0 ) { free(array); array = 0; @@ -2584,7 +2658,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt ((uint8_t *)&revtxid)[i] = ((uint8_t *)utxotxidp)[31 - i]; txNew.vin[0].prevout.hash = revtxid; txNew.vin[0].prevout.n = *utxovoutp; - txNew.vout[0].scriptPubKey = best_scriptPubKey;// CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG; + txNew.vout[0].scriptPubKey = best_scriptPubKey; txNew.vout[0].nValue = *utxovaluep - txfee; txNew.nLockTime = earliest; CTransaction txNewConst(txNew); @@ -2595,7 +2669,7 @@ int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blockt ptr = (uint8_t *)&sigdata.scriptSig[0]; siglen = sigdata.scriptSig.size(); for (i=0; i estimated height 1444000 +// 3: 3rd season ending isnt known, so use very far times in future. + // 1751328000 = dummy timestamp, 1 July 2025! + // 7113400 = 5x current KMD blockheight. +// to add 4th season, change NUM_KMD_SEASONS to 4, and add timestamp and height of activation to these arrays. +#define NUM_KMD_SEASONS 3 +#define NUM_KMD_NOTARIES 64 +static const uint32_t KMD_SEASON_TIMESTAMPS[NUM_KMD_SEASONS] = {1525132800, 1563148800, 1751328000}; +static const int32_t KMD_SEASON_HEIGHTS[NUM_KMD_SEASONS] = {814000, 1444000, 7113400}; + +// Era array of pubkeys. Add extra seasons to bottom as requried, after adding appropriate info above. +static const char *notaries_elected[NUM_KMD_SEASONS][NUM_KMD_NOTARIES][2] = +{ + { + { "0_jl777_testA", "03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828" }, + { "0_jl777_testB", "02ebfc784a4ba768aad88d44d1045d240d47b26e248cafaf1c5169a42d7a61d344" }, + { "0_kolo_testA", "0287aa4b73988ba26cf6565d815786caf0d2c4af704d7883d163ee89cd9977edec" }, + { "artik_AR", "029acf1dcd9f5ff9c455f8bb717d4ae0c703e089d16cf8424619c491dff5994c90" }, + { "artik_EU", "03f54b2c24f82632e3cdebe4568ba0acf487a80f8a89779173cdb78f74514847ce" }, + { "artik_NA", "0224e31f93eff0cc30eaf0b2389fbc591085c0e122c4d11862c1729d090106c842" }, + { "artik_SH", "02bdd8840a34486f38305f311c0e2ae73e84046f6e9c3dd3571e32e58339d20937" }, + { "badass_EU", "0209d48554768dd8dada988b98aca23405057ac4b5b46838a9378b95c3e79b9b9e" }, + { "badass_NA", "02afa1a9f948e1634a29dc718d218e9d150c531cfa852843a1643a02184a63c1a7" }, + { "badass_SH", "026b49dd3923b78a592c1b475f208e23698d3f085c4c3b4906a59faf659fd9530b" }, + { "crackers_EU", "03bc819982d3c6feb801ec3b720425b017d9b6ee9a40746b84422cbbf929dc73c3" }, // 10 + { "crackers_NA", "03205049103113d48c7c7af811b4c8f194dafc43a50d5313e61a22900fc1805b45" }, + { "crackers_SH", "02be28310e6312d1dd44651fd96f6a44ccc269a321f907502aae81d246fabdb03e" }, + { "durerus_EU", "02bcbd287670bdca2c31e5d50130adb5dea1b53198f18abeec7211825f47485d57" }, + { "etszombi_AR", "031c79168d15edabf17d9ec99531ea9baa20039d0cdc14d9525863b83341b210e9" }, + { "etszombi_EU", "0281b1ad28d238a2b217e0af123ce020b79e91b9b10ad65a7917216eda6fe64bf7" }, // 15 + { "etszombi_SH", "025d7a193c0757f7437fad3431f027e7b5ed6c925b77daba52a8755d24bf682dde" }, + { "farl4web_EU", "0300ecf9121cccf14cf9423e2adb5d98ce0c4e251721fa345dec2e03abeffbab3f" }, + { "farl4web_SH", "0396bb5ed3c57aa1221d7775ae0ff751e4c7dc9be220d0917fa8bbdf670586c030" }, + { "fullmoon_AR", "0254b1d64840ce9ff6bec9dd10e33beb92af5f7cee628f999cb6bc0fea833347cc" }, + { "fullmoon_NA", "031fb362323b06e165231c887836a8faadb96eda88a79ca434e28b3520b47d235b" }, // 20 + { "fullmoon_SH", "030e12b42ec33a80e12e570b6c8274ce664565b5c3da106859e96a7208b93afd0d" }, + { "grewal_NA", "03adc0834c203d172bce814df7c7a5e13dc603105e6b0adabc942d0421aefd2132" }, + { "grewal_SH", "03212a73f5d38a675ee3cdc6e82542a96c38c3d1c79d25a1ed2e42fcf6a8be4e68" }, + { "indenodes_AR", "02ec0fa5a40f47fd4a38ea5c89e375ad0b6ddf4807c99733c9c3dc15fb978ee147" }, + { "indenodes_EU", "0221387ff95c44cb52b86552e3ec118a3c311ca65b75bf807c6c07eaeb1be8303c" }, + { "indenodes_NA", "02698c6f1c9e43b66e82dbb163e8df0e5a2f62f3a7a882ca387d82f86e0b3fa988" }, + { "indenodes_SH", "0334e6e1ec8285c4b85bd6dae67e17d67d1f20e7328efad17ce6fd24ae97cdd65e" }, + { "jeezy_EU", "023cb3e593fb85c5659688528e9a4f1c4c7f19206edc7e517d20f794ba686fd6d6" }, + { "jsgalt_NA", "027b3fb6fede798cd17c30dbfb7baf9332b3f8b1c7c513f443070874c410232446" }, + { "karasugoi_NA", "02a348b03b9c1a8eac1b56f85c402b041c9bce918833f2ea16d13452309052a982" }, // 30 + { "kashifali_EU", "033777c52a0190f261c6f66bd0e2bb299d30f012dcb8bfff384103211edb8bb207" }, + { "kolo_AR", "03016d19344c45341e023b72f9fb6e6152fdcfe105f3b4f50b82a4790ff54e9dc6" }, + { "kolo_SH", "02aa24064500756d9b0959b44d5325f2391d8e95c6127e109184937152c384e185" }, + { "metaphilibert_AR", "02adad675fae12b25fdd0f57250b0caf7f795c43f346153a31fe3e72e7db1d6ac6" }, + { "movecrypto_AR", "022783d94518e4dc77cbdf1a97915b29f427d7bc15ea867900a76665d3112be6f3" }, + { "movecrypto_EU", "021ab53bc6cf2c46b8a5456759f9d608966eff87384c2b52c0ac4cc8dd51e9cc42" }, + { "movecrypto_NA", "02efb12f4d78f44b0542d1c60146738e4d5506d27ec98a469142c5c84b29de0a80" }, + { "movecrypto_SH", "031f9739a3ebd6037a967ce1582cde66e79ea9a0551c54731c59c6b80f635bc859" }, + { "muros_AR", "022d77402fd7179335da39479c829be73428b0ef33fb360a4de6890f37c2aa005e" }, + { "noashh_AR", "029d93ef78197dc93892d2a30e5a54865f41e0ca3ab7eb8e3dcbc59c8756b6e355" }, // 40 + { "noashh_EU", "02061c6278b91fd4ac5cab4401100ffa3b2d5a277e8f71db23401cc071b3665546" }, + { "noashh_NA", "033c073366152b6b01535e15dd966a3a8039169584d06e27d92a69889b720d44e1" }, + { "nxtswe_EU", "032fb104e5eaa704a38a52c126af8f67e870d70f82977e5b2f093d5c1c21ae5899" }, + { "polycryptoblog_NA", "02708dcda7c45fb54b78469673c2587bfdd126e381654819c4c23df0e00b679622" }, + { "pondsea_AR", "032e1c213787312099158f2d74a89e8240a991d162d4ce8017d8504d1d7004f735" }, + { "pondsea_EU", "0225aa6f6f19e543180b31153d9e6d55d41bc7ec2ba191fd29f19a2f973544e29d" }, + { "pondsea_NA", "031bcfdbb62268e2ff8dfffeb9ddff7fe95fca46778c77eebff9c3829dfa1bb411" }, + { "pondsea_SH", "02209073bc0943451498de57f802650311b1f12aa6deffcd893da198a544c04f36" }, + { "popcornbag_AR", "02761f106fb34fbfc5ddcc0c0aa831ed98e462a908550b280a1f7bd32c060c6fa3" }, + { "popcornbag_NA", "03c6085c7fdfff70988fda9b197371f1caf8397f1729a844790e421ee07b3a93e8" }, // 50 + { "ptytrader_NA", "0328c61467148b207400b23875234f8a825cce65b9c4c9b664f47410b8b8e3c222" }, + { "ptytrader_SH", "0250c93c492d8d5a6b565b90c22bee07c2d8701d6118c6267e99a4efd3c7748fa4" }, + { "rnr_AR", "029bdb08f931c0e98c2c4ba4ef45c8e33a34168cb2e6bf953cef335c359d77bfcd" }, + { "rnr_EU", "03f5c08dadffa0ffcafb8dd7ffc38c22887bd02702a6c9ac3440deddcf2837692b" }, + { "rnr_NA", "02e17c5f8c3c80f584ed343b8dcfa6d710dfef0889ec1e7728ce45ce559347c58c" }, + { "rnr_SH", "037536fb9bdfed10251f71543fb42679e7c52308bcd12146b2568b9a818d8b8377" }, + { "titomane_AR", "03cda6ca5c2d02db201488a54a548dbfc10533bdc275d5ea11928e8d6ab33c2185" }, + { "titomane_EU", "02e41feded94f0cc59f55f82f3c2c005d41da024e9a805b41105207ef89aa4bfbd" }, + { "titomane_SH", "035f49d7a308dd9a209e894321f010d21b7793461b0c89d6d9231a3fe5f68d9960" }, + { "vanbreuk_EU", "024f3cad7601d2399c131fd070e797d9cd8533868685ddbe515daa53c2e26004c3" }, // 60 + { "xrobesx_NA", "03f0cc6d142d14a40937f12dbd99dbd9021328f45759e26f1877f2a838876709e1" }, + { "xxspot1_XX", "02ef445a392fcaf3ad4176a5da7f43580e8056594e003eba6559a713711a27f955" }, + { "xxspot2_XX", "03d85b221ea72ebcd25373e7961f4983d12add66a92f899deaf07bab1d8b6f5573" } + }, + { + {"0dev1_jl777", "03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828" }, + {"0dev2_kolo", "030f34af4b908fb8eb2099accb56b8d157d49f6cfb691baa80fdd34f385efed961" }, + {"0dev3_kolo", "025af9d2b2a05338478159e9ac84543968fd18c45fd9307866b56f33898653b014" }, + {"0dev4_decker", "028eea44a09674dda00d88ffd199a09c9b75ba9782382cc8f1e97c0fd565fe5707" }, + {"a-team_SH", "03b59ad322b17cb94080dc8e6dc10a0a865de6d47c16fb5b1a0b5f77f9507f3cce" }, + {"artik_AR", "029acf1dcd9f5ff9c455f8bb717d4ae0c703e089d16cf8424619c491dff5994c90" }, + {"artik_EU", "03f54b2c24f82632e3cdebe4568ba0acf487a80f8a89779173cdb78f74514847ce" }, + {"artik_NA", "0224e31f93eff0cc30eaf0b2389fbc591085c0e122c4d11862c1729d090106c842" }, + {"artik_SH", "02bdd8840a34486f38305f311c0e2ae73e84046f6e9c3dd3571e32e58339d20937" }, + {"badass_EU", "0209d48554768dd8dada988b98aca23405057ac4b5b46838a9378b95c3e79b9b9e" }, + {"badass_NA", "02afa1a9f948e1634a29dc718d218e9d150c531cfa852843a1643a02184a63c1a7" }, // 10 + {"batman_AR", "033ecb640ec5852f42be24c3bf33ca123fb32ced134bed6aa2ba249cf31b0f2563" }, + {"batman_SH", "02ca5898931181d0b8aafc75ef56fce9c43656c0b6c9f64306e7c8542f6207018c" }, + {"ca333_EU", "03fc87b8c804f12a6bd18efd43b0ba2828e4e38834f6b44c0bfee19f966a12ba99" }, + {"chainmakers_EU", "02f3b08938a7f8d2609d567aebc4989eeded6e2e880c058fdf092c5da82c3bc5ee" }, + {"chainmakers_NA", "0276c6d1c65abc64c8559710b8aff4b9e33787072d3dda4ec9a47b30da0725f57a" }, + {"chainstrike_SH", "0370bcf10575d8fb0291afad7bf3a76929734f888228bc49e35c5c49b336002153" }, + {"cipi_AR", "02c4f89a5b382750836cb787880d30e23502265054e1c327a5bfce67116d757ce8" }, + {"cipi_NA", "02858904a2a1a0b44df4c937b65ee1f5b66186ab87a751858cf270dee1d5031f18" }, + {"crackers_EU", "03bc819982d3c6feb801ec3b720425b017d9b6ee9a40746b84422cbbf929dc73c3" }, + {"crackers_NA", "03205049103113d48c7c7af811b4c8f194dafc43a50d5313e61a22900fc1805b45" }, // 20 + {"dwy_EU", "0259c646288580221fdf0e92dbeecaee214504fdc8bbdf4a3019d6ec18b7540424" }, + {"emmanux_SH", "033f316114d950497fc1d9348f03770cd420f14f662ab2db6172df44c389a2667a" }, + {"etszombi_EU", "0281b1ad28d238a2b217e0af123ce020b79e91b9b10ad65a7917216eda6fe64bf7" }, + {"fullmoon_AR", "03380314c4f42fa854df8c471618751879f9e8f0ff5dbabda2bd77d0f96cb35676" }, + {"fullmoon_NA", "030216211d8e2a48bae9e5d7eb3a42ca2b7aae8770979a791f883869aea2fa6eef" }, + {"fullmoon_SH", "03f34282fa57ecc7aba8afaf66c30099b5601e98dcbfd0d8a58c86c20d8b692c64" }, + {"goldenman_EU", "02d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388d" }, + {"indenodes_AR", "02ec0fa5a40f47fd4a38ea5c89e375ad0b6ddf4807c99733c9c3dc15fb978ee147" }, + {"indenodes_EU", "0221387ff95c44cb52b86552e3ec118a3c311ca65b75bf807c6c07eaeb1be8303c" }, + {"indenodes_NA", "02698c6f1c9e43b66e82dbb163e8df0e5a2f62f3a7a882ca387d82f86e0b3fa988" }, // 30 + {"indenodes_SH", "0334e6e1ec8285c4b85bd6dae67e17d67d1f20e7328efad17ce6fd24ae97cdd65e" }, + {"jackson_AR", "038ff7cfe34cb13b524e0941d5cf710beca2ffb7e05ddf15ced7d4f14fbb0a6f69" }, + {"jeezy_EU", "023cb3e593fb85c5659688528e9a4f1c4c7f19206edc7e517d20f794ba686fd6d6" }, + {"karasugoi_NA", "02a348b03b9c1a8eac1b56f85c402b041c9bce918833f2ea16d13452309052a982" }, + {"komodoninja_EU", "038e567b99806b200b267b27bbca2abf6a3e8576406df5f872e3b38d30843cd5ba" }, + {"komodoninja_SH", "033178586896915e8456ebf407b1915351a617f46984001790f0cce3d6f3ada5c2" }, + {"komodopioneers_SH", "033ace50aedf8df70035b962a805431363a61cc4e69d99d90726a2d48fb195f68c" }, + {"libscott_SH", "03301a8248d41bc5dc926088a8cf31b65e2daf49eed7eb26af4fb03aae19682b95" }, + {"lukechilds_AR", "031aa66313ee024bbee8c17915cf7d105656d0ace5b4a43a3ab5eae1e14ec02696" }, + {"madmax_AR", "03891555b4a4393d655bf76f0ad0fb74e5159a615b6925907678edc2aac5e06a75" }, // 40 + {"meshbits_AR", "02957fd48ae6cb361b8a28cdb1b8ccf5067ff68eb1f90cba7df5f7934ed8eb4b2c" }, + {"meshbits_SH", "025c6e94877515dfd7b05682b9cc2fe4a49e076efe291e54fcec3add78183c1edb" }, + {"metaphilibert_AR", "02adad675fae12b25fdd0f57250b0caf7f795c43f346153a31fe3e72e7db1d6ac6" }, + {"metaphilibert_SH", "0284af1a5ef01503e6316a2ca4abf8423a794e9fc17ac6846f042b6f4adedc3309" }, + {"patchkez_SH", "0296270f394140640f8fa15684fc11255371abb6b9f253416ea2734e34607799c4" }, + {"pbca26_NA", "0276aca53a058556c485bbb60bdc54b600efe402a8b97f0341a7c04803ce204cb5" }, + {"peer2cloud_AR", "034e5563cb885999ae1530bd66fab728e580016629e8377579493b386bf6cebb15" }, + {"peer2cloud_SH", "03396ac453b3f23e20f30d4793c5b8ab6ded6993242df4f09fd91eb9a4f8aede84" }, + {"polycryptoblog_NA", "02708dcda7c45fb54b78469673c2587bfdd126e381654819c4c23df0e00b679622" }, + {"hyper_AR", "020f2f984d522051bd5247b61b080b4374a7ab389d959408313e8062acad3266b4" }, // 50 + {"hyper_EU", "03d00cf9ceace209c59fb013e112a786ad583d7de5ca45b1e0df3b4023bb14bf51" }, + {"hyper_SH", "0383d0b37f59f4ee5e3e98a47e461c861d49d0d90c80e9e16f7e63686a2dc071f3" }, + {"hyper_NA", "03d91c43230336c0d4b769c9c940145a8c53168bf62e34d1bccd7f6cfc7e5592de" }, + {"popcornbag_AR", "02761f106fb34fbfc5ddcc0c0aa831ed98e462a908550b280a1f7bd32c060c6fa3" }, + {"popcornbag_NA", "03c6085c7fdfff70988fda9b197371f1caf8397f1729a844790e421ee07b3a93e8" }, + {"alien_AR", "0348d9b1fc6acf81290405580f525ee49b4749ed4637b51a28b18caa26543b20f0" }, + {"alien_EU", "020aab8308d4df375a846a9e3b1c7e99597b90497efa021d50bcf1bbba23246527" }, + {"thegaltmines_NA", "031bea28bec98b6380958a493a703ddc3353d7b05eb452109a773eefd15a32e421" }, + {"titomane_AR", "029d19215440d8cb9cc6c6b7a4744ae7fb9fb18d986e371b06aeb34b64845f9325" }, + {"titomane_EU", "0360b4805d885ff596f94312eed3e4e17cb56aa8077c6dd78d905f8de89da9499f" }, // 60 + {"titomane_SH", "03573713c5b20c1e682a2e8c0f8437625b3530f278e705af9b6614de29277a435b" }, + {"webworker01_NA", "03bb7d005e052779b1586f071834c5facbb83470094cff5112f0072b64989f97d7" }, + {"xrobesx_NA", "03f0cc6d142d14a40937f12dbd99dbd9021328f45759e26f1877f2a838876709e1" }, + }, + { + {"madmax_NA", "02016371f90f195ed9aeaf37be06f3fbbfbfe2ef8a4fd66378378289476e7751db" }, + {"alright_AR", "020566fe2fb3874258b2d3cf1809a5d650e0edc7ba746fa5eec72750c5188c9cc9" }, + {"strob_NA", "0206f7a2e972d9dfef1c424c731503a0a27de1ba7a15a91a362dc7ec0d0fb47685" }, + {"dwy_EU", "020a77f454a1d3fcdc0312048fc58d713fe6153189e0c59cdd12fedbf09553d4b0" }, + {"phm87_SH", "021773a38db1bc3ede7f28142f901a161c7b7737875edbb40082a201c55dcf0add" }, + {"chainmakers_NA", "021ad488cc063e17d0f1a2aade6be65ef8b5ff879987cb0b86bb700733099fe524" }, + {"indenodes_EU", "0221387ff95c44cb52b86552e3ec118a3c311ca65b75bf807c6c07eaeb1be8303c" }, + {"blackjok3r_SH", "0226293d7644e1380555ecc2d385ba038a66f4202b24b643ab31bcad0dc1474de2" }, + {"chainmakers_EU", "022a6b583e8820d14d911a11820fd80853c5b8406e91a0a6fcac1f133d098d75cb" }, + {"titomane_AR", "023e3aa9834c46971ff3e7cb86a200ec9c8074a9566a3ea85d400d5739662ee989" }, + {"fullmoon_SH", "02639998420688ee935d279d1cd52f1c6f3ae12c1f3afc20b3bf7ece1057f8b93b" }, // 10 + {"indenodes_NA", "02698c6f1c9e43b66e82dbb163e8df0e5a2f62f3a7a882ca387d82f86e0b3fa988" }, + {"chmex_EU", "0281304ebbcc39e4f09fda85f4232dd8dacd668e20e5fc11fba6b985186c90086e" }, + {"metaphilibert_SH", "0284af1a5ef01503e6316a2ca4abf8423a794e9fc17ac6846f042b6f4adedc3309" }, + {"ca333_DEV", "02856843af2d9457b5b1c907068bef6077ea0904cc8bd4df1ced013f64bf267958" }, + {"cipi_NA", "02858904a2a1a0b44df4c937b65ee1f5b66186ab87a751858cf270dee1d5031f18" }, + {"pungocloud_SH", "02863628f842a8aa424daf745cf43a1717cfcd571338ba87bfcec4d340703d01d6" }, + {"voskcoin_EU", "028cfb0bbce2207425bc8d40fff634269bb61aade2d1d03883398275550b465600" }, + {"decker_DEV", "028eea44a09674dda00d88ffd199a09c9b75ba9782382cc8f1e97c0fd565fe5707" }, + {"cryptoeconomy_EU", "0290ab4937e85246e048552df3e9a66cba2c1602db76e03763e16c671e750145d1" }, + {"etszombi_EU", "0293ea48d8841af7a419a24d9da11c34b39127ef041f847651bae6ab14dcd1f6b4" }, // 20 + {"karasugoi_NA", "02a348b03b9c1a8eac1b56f85c402b041c9bce918833f2ea16d13452309052a982" }, + {"pirate_AR", "02a5e865af771eee0c6bd3bd238f01f5eb589907ba52efa9f37e5102375c8eb7a0" }, + {"metaphilibert_AR", "02adad675fae12b25fdd0f57250b0caf7f795c43f346153a31fe3e72e7db1d6ac6" }, + {"zatjum_SH", "02b315714f99fdd70bafd50a4c3aa4b9db0fd8d63978bd8d47763773400b28734c" }, + {"madmax_AR", "02b37913ac0250d17cca5bb94ef65928b1515f13f839075446db73b9b067c13aa0" }, + {"lukechilds_NA", "02c191a085bbb0bd86d126c62d3e801c503038b69c0c2de121c9a7abe3e6684420" }, + {"cipi_AR", "02c4f89a5b382750836cb787880d30e23502265054e1c327a5bfce67116d757ce8" }, + {"tonyl_AR", "02cc8bc862f2b65ad4f99d5f68d3011c138bf517acdc8d4261166b0be8f64189e1" }, + {"infotech_DEV", "02d7d63ac441dbcd5535bcad741701519c190e89e17e4180aa0f61fdda0b6195ea" }, + {"fullmoon_NA", "02da228a67e519b21bca6426ed982509a21d24d45f1ac1bdf698b6083fed442ec3" }, // 30 + {"etszombi_AR", "02e55e104aa94f70cde68165d7df3e162d4410c76afd4643b161dea044aa6d06ce" }, + {"node-9_EU", "02e90f0528f8fd7d2b7f28d53ed7368cc4b1dfc5814c70c486c533862ee05e37dc" }, + {"phba2061_EU", "02eaa599f5434039adc5748b3b830f605342e11b51273046b04f24acc5bcecc74a" }, + {"indenodes_AR", "02ec0fa5a40f47fd4a38ea5c89e375ad0b6ddf4807c99733c9c3dc15fb978ee147" }, + {"and1-89_EU", "02f2af778c62888cfb106c0ef5c89cefe012ef3e6abed3ef28425092637d7ae3cc" }, + {"komodopioneers_SH", "02ffde73d51b56bf9e23b6f03008ac7916cacd3be090f44eab59877792e08b6751" }, + {"komodopioneers_EU", "03014f3fceb9f851f9dba5f5b371c7758b3f1fe44a70b4e60e3d6c70d1e5265358" }, + {"d0ct0r_NA", "0303725d8525b6f969122faf04152653eb4bf34e10de92182263321769c334bf58" }, + {"kolo_DEV", "030f34af4b908fb8eb2099accb56b8d157d49f6cfb691baa80fdd34f385efed961" }, + {"peer2cloud_AR", "030fe3c77db0f91d79c26f951b674bba557b899f16570cd75332829a07031b0097" }, // 40 + {"webworker01_SH", "031e50ba6de3c16f99d414bb89866e578d963a54bde7916c810608966fb5700776" }, + {"webworker01_NA", "032735e9cad1bb00eaababfa6d27864fa4c1db0300c85e01e52176be2ca6a243ce" }, + {"pbca26_NA", "0331c51abb78a9891d1b9483747a7fd9bbbbdd5256f40af23c87384e3d9a974289" }, + {"indenodes_SH", "0334e6e1ec8285c4b85bd6dae67e17d67d1f20e7328efad17ce6fd24ae97cdd65e" }, + {"pirate_NA", "033c8cef71f704f357c9ac1aeaafde3acd7bbfd0a09494e5e129ee0999fb2bc368" }, + {"lukechilds_AR", "035004d780316008c8afd5ac28d08aae56f4746bf70eb2c999e3c7d9a7a50ca8b6" }, + {"dragonhound_NA", "0354c779b9b111fdc825ee0250b33a79a837e0775a111a89916853d02a8999285c" }, + {"fullmoon_AR", "035673e913d603137f7f517d88bf316e51d408e0ec2b2cdcbfbdd5c0b553fc9351" }, + {"chainzilla_SH", "0360804b8817fd25ded6e9c0b50e3b0782ac666545b5416644198e18bc3903d9f9" }, + {"titomane_EU", "03772ac0aad6b0e9feec5e591bff5de6775d6132e888633e73d3ba896bdd8e0afb" }, // 50 + {"jeezy_EU", "037f182facbad35684a6e960699f5da4ba89e99f0d0d62a87e8400dd086c8e5dd7" }, + {"titomane_SH", "03850fdddf2413b51790daf51dd30823addb37313c8854b508ea6228205047ef9b" }, + {"alien_AR", "03911a60395801082194b6834244fa78a3c30ff3e888667498e157b4aa80b0a65f" }, + {"pirate_EU", "03986339bebe3891e369a528356a220bc17a5bef3d337edfcf0114e66e6b1c120f" }, + {"thegaltmines_NA", "03b113c7aa1042c965154bf791713f562891ac75e60ab78886205535bcaabb5013" }, + {"computergenie_NA", "03b41c764c8cd63b48b79f15f112a9b5105a0dce35df7d6990d6919503a1f3f245" }, + {"nutellalicka_SH", "03b422ec85a8465ae42e7004bc6d0856a458f0f920ec0f58fd4d5613994f3faa5b" }, + {"chainstrike_SH", "03b806be3bf7a1f2f6290ec5c1ea7d3ea57774dcfcf2129a82b2569e585100e1cb" }, + {"dwy_SH", "03b9162f9713d7cddd89aaad0398b67f66377cc17547ec0643a67c2d6fb099ecf0" }, // 60 + {"alien_EU", "03bb749e337b9074465fa28e757b5aa92cb1f0fea1a39589bca91a602834d443cd" }, + {"gt_AR", "03e22a6aad65c9a4c2ce2e724b26edf631b5f41253449dd5feaeba68e7c0cf3058" }, + {"patchkez_SH", "03f45e9beb5c4cd46525db8195eb05c1db84ae7ef3603566b3d775770eba3b96ee" }, + {"decker_AR", "03ffdf1a116300a78729608d9930742cd349f11a9d64fcc336b8f18592dd9c91bc" }, + } +}; + #define SETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] |= (1 << ((bitoffset) & 7))) #define GETBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] & (1 << ((bitoffset) & 7))) #define CLEARBIT(bits,bitoffset) (((uint8_t *)bits)[(bitoffset) >> 3] &= ~(1 << ((bitoffset) & 7))) @@ -38,7 +253,12 @@ #define KOMODO_MAXNVALUE (((uint64_t)1 << 63) - 1) #define KOMODO_BIT63SET(x) ((x) & ((uint64_t)1 << 63)) #define KOMODO_VALUETOOBIG(x) ((x) > (uint64_t)10000000001*COIN) + +//#ifndef TESTMODE #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +//#else +//#define PRICES_DAYWINDOW (7) +//#endif extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); @@ -63,7 +283,7 @@ extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS extern std::vector ASSETCHAINS_PRICES,ASSETCHAINS_STOCKS; extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; - +extern uint256 KOMODO_EARLYTXID; extern int32_t KOMODO_CONNECTING,KOMODO_CCACTIVATE,KOMODO_DEALERNODE; extern uint32_t ASSETCHAINS_CC; @@ -77,11 +297,14 @@ extern int32_t VERUS_MIN_STAKEAGE; extern std::string DONATION_PUBKEY; extern uint8_t ASSETCHAINS_PRIVATE; extern int32_t USE_EXTERNAL_PUBKEY; -extern char NOTARYADDRS[64][64]; -extern int32_t KOMODO_TESTNODE; +extern char NOTARYADDRS[64][64]; // should be depreciated later. Only affects labs. +extern char NOTARY_ADDRESSES[NUM_KMD_SEASONS][64][64]; +extern int32_t KOMODO_TESTNODE, KOMODO_SNAPSHOT_INTERVAL; +extern int32_t ASSETCHAINS_EARLYTXIDCONTRACT; int tx_height( const uint256 &hash ); extern std::vector vWhiteListAddress; void komodo_netevent(std::vector payload); +int32_t getacseason(uint32_t timestamp); #define IGUANA_MAXSCRIPTSIZE 10001 #define KOMODO_KVDURATION 1440 @@ -101,7 +324,7 @@ int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); int8_t komodo_segid(int32_t nocache,int32_t height); int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); -int32_t komodo_priceind(char *symbol); +int32_t komodo_priceind(const char *symbol); int32_t komodo_pricesinit(); int64_t komodo_priceave(int64_t *tmpbuf,int64_t *correlated,int32_t cskip); int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t rawskip,uint32_t *nonzprices,int32_t smoothwidth); diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0ff1c0b66..7cfe870b4 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2017,8 +2017,10 @@ int32_t get_stockprices(uint32_t now,uint32_t *prices,std::vector s { char url[32768],*symbol,*timestr; cJSON *json,*obj; int32_t i,n=0,retval=-1; uint32_t uprice,timestamp; sprintf(url,"https://api.iextrading.com/1.0/tops/last?symbols=%s",GetArg("-ac_stocks","").c_str()); + fprintf(stderr,"url.(%s)\n",url); if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"iex")) != 0 ) // { + fprintf(stderr,"stocks.(%s)\n",jprint(json,0)); if ( (n= cJSON_GetArraySize(json)) > 0 ) { retval = n; @@ -2400,8 +2402,8 @@ char *komodo_pricename(char *name,int32_t ind) } return(0); } - -int32_t komodo_priceind(char *symbol) +// finds index for its symbol name +int32_t komodo_priceind(const char *symbol) { char name[65]; int32_t i,n = (int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET) / sizeof(uint32_t)); for (i=1; i Mineropret; std::vector vWhiteListAddress; char NOTARYADDRS[64][64]; +char NOTARY_ADDRESSES[NUM_KMD_SEASONS][64][64]; char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN],ASSETCHAINS_USERPASS[4096]; uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,ASSETCHAINS_BEAMPORT,ASSETCHAINS_CODAPORT; @@ -109,7 +110,9 @@ uint64_t PENDING_KOMODO_TX; extern int32_t KOMODO_LOADINGBLOCKS; unsigned int MAX_BLOCK_SIGOPS = 20000; -int32_t KOMODO_TESTNODE; +int32_t KOMODO_TESTNODE, KOMODO_SNAPSHOT_INTERVAL; +CScript KOMODO_EARLYTXID_SCRIPTPUB; +int32_t ASSETCHAINS_EARLYTXIDCONTRACT; struct komodo_kv *KOMODO_KV; pthread_mutex_t KOMODO_KV_mutex,KOMODO_CC_mutex; diff --git a/src/komodo_nk.h b/src/komodo_nk.h index 3c9034dde..68512dfd7 100644 --- a/src/komodo_nk.h +++ b/src/komodo_nk.h @@ -1,6 +1,9 @@ #ifndef KOMODO_NK_H #define KOMODO_NK_H +//#define ASSETCHAINS_N 77 +//#define ASSETCHAINS_K 3 + #define ASSETCHAINS_N 96 #define ASSETCHAINS_K 5 diff --git a/src/komodo_notary.h b/src/komodo_notary.h index 5cdbf237d..028bc4dc2 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -20,6 +20,7 @@ #include "notaries_staked.h" #define KOMODO_MAINNET_START 178999 +#define KOMODO_NOTARIES_HEIGHT1 814000 const char *Notaries_genesis[][2] = { @@ -60,194 +61,78 @@ const char *Notaries_genesis[][2] = { "titomane_SH", "035f49d7a308dd9a209e894321f010d21b7793461b0c89d6d9231a3fe5f68d9960" }, }; -const char *Notaries_elected0[][2] = -{ - { "0_jl777_testA", "03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828" }, - { "0_jl777_testB", "02ebfc784a4ba768aad88d44d1045d240d47b26e248cafaf1c5169a42d7a61d344" }, - { "0_kolo_testA", "0287aa4b73988ba26cf6565d815786caf0d2c4af704d7883d163ee89cd9977edec" }, - { "artik_AR", "029acf1dcd9f5ff9c455f8bb717d4ae0c703e089d16cf8424619c491dff5994c90" }, - { "artik_EU", "03f54b2c24f82632e3cdebe4568ba0acf487a80f8a89779173cdb78f74514847ce" }, - { "artik_NA", "0224e31f93eff0cc30eaf0b2389fbc591085c0e122c4d11862c1729d090106c842" }, - { "artik_SH", "02bdd8840a34486f38305f311c0e2ae73e84046f6e9c3dd3571e32e58339d20937" }, - { "badass_EU", "0209d48554768dd8dada988b98aca23405057ac4b5b46838a9378b95c3e79b9b9e" }, - { "badass_NA", "02afa1a9f948e1634a29dc718d218e9d150c531cfa852843a1643a02184a63c1a7" }, - { "badass_SH", "026b49dd3923b78a592c1b475f208e23698d3f085c4c3b4906a59faf659fd9530b" }, - { "crackers_EU", "03bc819982d3c6feb801ec3b720425b017d9b6ee9a40746b84422cbbf929dc73c3" }, // 10 - { "crackers_NA", "03205049103113d48c7c7af811b4c8f194dafc43a50d5313e61a22900fc1805b45" }, - { "crackers_SH", "02be28310e6312d1dd44651fd96f6a44ccc269a321f907502aae81d246fabdb03e" }, - { "durerus_EU", "02bcbd287670bdca2c31e5d50130adb5dea1b53198f18abeec7211825f47485d57" }, - { "etszombi_AR", "031c79168d15edabf17d9ec99531ea9baa20039d0cdc14d9525863b83341b210e9" }, - { "etszombi_EU", "0281b1ad28d238a2b217e0af123ce020b79e91b9b10ad65a7917216eda6fe64bf7" }, // 15 - { "etszombi_SH", "025d7a193c0757f7437fad3431f027e7b5ed6c925b77daba52a8755d24bf682dde" }, - { "farl4web_EU", "0300ecf9121cccf14cf9423e2adb5d98ce0c4e251721fa345dec2e03abeffbab3f" }, - { "farl4web_SH", "0396bb5ed3c57aa1221d7775ae0ff751e4c7dc9be220d0917fa8bbdf670586c030" }, - { "fullmoon_AR", "0254b1d64840ce9ff6bec9dd10e33beb92af5f7cee628f999cb6bc0fea833347cc" }, - { "fullmoon_NA", "031fb362323b06e165231c887836a8faadb96eda88a79ca434e28b3520b47d235b" }, // 20 - { "fullmoon_SH", "030e12b42ec33a80e12e570b6c8274ce664565b5c3da106859e96a7208b93afd0d" }, - { "grewal_NA", "03adc0834c203d172bce814df7c7a5e13dc603105e6b0adabc942d0421aefd2132" }, - { "grewal_SH", "03212a73f5d38a675ee3cdc6e82542a96c38c3d1c79d25a1ed2e42fcf6a8be4e68" }, - { "indenodes_AR", "02ec0fa5a40f47fd4a38ea5c89e375ad0b6ddf4807c99733c9c3dc15fb978ee147" }, - { "indenodes_EU", "0221387ff95c44cb52b86552e3ec118a3c311ca65b75bf807c6c07eaeb1be8303c" }, - { "indenodes_NA", "02698c6f1c9e43b66e82dbb163e8df0e5a2f62f3a7a882ca387d82f86e0b3fa988" }, - { "indenodes_SH", "0334e6e1ec8285c4b85bd6dae67e17d67d1f20e7328efad17ce6fd24ae97cdd65e" }, - { "jeezy_EU", "023cb3e593fb85c5659688528e9a4f1c4c7f19206edc7e517d20f794ba686fd6d6" }, - { "jsgalt_NA", "027b3fb6fede798cd17c30dbfb7baf9332b3f8b1c7c513f443070874c410232446" }, - { "karasugoi_NA", "02a348b03b9c1a8eac1b56f85c402b041c9bce918833f2ea16d13452309052a982" }, // 30 - { "kashifali_EU", "033777c52a0190f261c6f66bd0e2bb299d30f012dcb8bfff384103211edb8bb207" }, - { "kolo_AR", "03016d19344c45341e023b72f9fb6e6152fdcfe105f3b4f50b82a4790ff54e9dc6" }, - { "kolo_SH", "02aa24064500756d9b0959b44d5325f2391d8e95c6127e109184937152c384e185" }, - { "metaphilibert_AR", "02adad675fae12b25fdd0f57250b0caf7f795c43f346153a31fe3e72e7db1d6ac6" }, - { "movecrypto_AR", "022783d94518e4dc77cbdf1a97915b29f427d7bc15ea867900a76665d3112be6f3" }, - { "movecrypto_EU", "021ab53bc6cf2c46b8a5456759f9d608966eff87384c2b52c0ac4cc8dd51e9cc42" }, - { "movecrypto_NA", "02efb12f4d78f44b0542d1c60146738e4d5506d27ec98a469142c5c84b29de0a80" }, - { "movecrypto_SH", "031f9739a3ebd6037a967ce1582cde66e79ea9a0551c54731c59c6b80f635bc859" }, - { "muros_AR", "022d77402fd7179335da39479c829be73428b0ef33fb360a4de6890f37c2aa005e" }, - { "noashh_AR", "029d93ef78197dc93892d2a30e5a54865f41e0ca3ab7eb8e3dcbc59c8756b6e355" }, // 40 - { "noashh_EU", "02061c6278b91fd4ac5cab4401100ffa3b2d5a277e8f71db23401cc071b3665546" }, - { "noashh_NA", "033c073366152b6b01535e15dd966a3a8039169584d06e27d92a69889b720d44e1" }, - { "nxtswe_EU", "032fb104e5eaa704a38a52c126af8f67e870d70f82977e5b2f093d5c1c21ae5899" }, - { "polycryptoblog_NA", "02708dcda7c45fb54b78469673c2587bfdd126e381654819c4c23df0e00b679622" }, - { "pondsea_AR", "032e1c213787312099158f2d74a89e8240a991d162d4ce8017d8504d1d7004f735" }, - { "pondsea_EU", "0225aa6f6f19e543180b31153d9e6d55d41bc7ec2ba191fd29f19a2f973544e29d" }, - { "pondsea_NA", "031bcfdbb62268e2ff8dfffeb9ddff7fe95fca46778c77eebff9c3829dfa1bb411" }, - { "pondsea_SH", "02209073bc0943451498de57f802650311b1f12aa6deffcd893da198a544c04f36" }, - { "popcornbag_AR", "02761f106fb34fbfc5ddcc0c0aa831ed98e462a908550b280a1f7bd32c060c6fa3" }, - { "popcornbag_NA", "03c6085c7fdfff70988fda9b197371f1caf8397f1729a844790e421ee07b3a93e8" }, // 50 - { "ptytrader_NA", "0328c61467148b207400b23875234f8a825cce65b9c4c9b664f47410b8b8e3c222" }, - { "ptytrader_SH", "0250c93c492d8d5a6b565b90c22bee07c2d8701d6118c6267e99a4efd3c7748fa4" }, - { "rnr_AR", "029bdb08f931c0e98c2c4ba4ef45c8e33a34168cb2e6bf953cef335c359d77bfcd" }, - { "rnr_EU", "03f5c08dadffa0ffcafb8dd7ffc38c22887bd02702a6c9ac3440deddcf2837692b" }, - { "rnr_NA", "02e17c5f8c3c80f584ed343b8dcfa6d710dfef0889ec1e7728ce45ce559347c58c" }, - { "rnr_SH", "037536fb9bdfed10251f71543fb42679e7c52308bcd12146b2568b9a818d8b8377" }, - { "titomane_AR", "03cda6ca5c2d02db201488a54a548dbfc10533bdc275d5ea11928e8d6ab33c2185" }, - { "titomane_EU", "02e41feded94f0cc59f55f82f3c2c005d41da024e9a805b41105207ef89aa4bfbd" }, - { "titomane_SH", "035f49d7a308dd9a209e894321f010d21b7793461b0c89d6d9231a3fe5f68d9960" }, - { "vanbreuk_EU", "024f3cad7601d2399c131fd070e797d9cd8533868685ddbe515daa53c2e26004c3" }, // 60 - { "xrobesx_NA", "03f0cc6d142d14a40937f12dbd99dbd9021328f45759e26f1877f2a838876709e1" }, - { "xxspot1_XX", "02ef445a392fcaf3ad4176a5da7f43580e8056594e003eba6559a713711a27f955" }, - { "xxspot2_XX", "03d85b221ea72ebcd25373e7961f4983d12add66a92f899deaf07bab1d8b6f5573" } -}; - -#define KOMODO_NOTARIES_TIMESTAMP1 1525132800 // May 1st 2018 1530921600 // 7/7/2017 -#define KOMODO_NOTARIES_HEIGHT1 ((814000 / KOMODO_ELECTION_GAP) * KOMODO_ELECTION_GAP) - -const char *Notaries_elected1[][2] = -{ - {"0dev1_jl777", "03b7621b44118017a16043f19b30cc8a4cfe068ac4e42417bae16ba460c80f3828" }, - {"0dev2_kolo", "030f34af4b908fb8eb2099accb56b8d157d49f6cfb691baa80fdd34f385efed961" }, - {"0dev3_kolo", "025af9d2b2a05338478159e9ac84543968fd18c45fd9307866b56f33898653b014" }, - {"0dev4_decker", "028eea44a09674dda00d88ffd199a09c9b75ba9782382cc8f1e97c0fd565fe5707" }, - {"a-team_SH", "03b59ad322b17cb94080dc8e6dc10a0a865de6d47c16fb5b1a0b5f77f9507f3cce" }, - {"artik_AR", "029acf1dcd9f5ff9c455f8bb717d4ae0c703e089d16cf8424619c491dff5994c90" }, - {"artik_EU", "03f54b2c24f82632e3cdebe4568ba0acf487a80f8a89779173cdb78f74514847ce" }, - {"artik_NA", "0224e31f93eff0cc30eaf0b2389fbc591085c0e122c4d11862c1729d090106c842" }, - {"artik_SH", "02bdd8840a34486f38305f311c0e2ae73e84046f6e9c3dd3571e32e58339d20937" }, - {"badass_EU", "0209d48554768dd8dada988b98aca23405057ac4b5b46838a9378b95c3e79b9b9e" }, - {"badass_NA", "02afa1a9f948e1634a29dc718d218e9d150c531cfa852843a1643a02184a63c1a7" }, // 10 - {"batman_AR", "033ecb640ec5852f42be24c3bf33ca123fb32ced134bed6aa2ba249cf31b0f2563" }, - {"batman_SH", "02ca5898931181d0b8aafc75ef56fce9c43656c0b6c9f64306e7c8542f6207018c" }, - {"ca333_EU", "03fc87b8c804f12a6bd18efd43b0ba2828e4e38834f6b44c0bfee19f966a12ba99" }, - {"chainmakers_EU", "02f3b08938a7f8d2609d567aebc4989eeded6e2e880c058fdf092c5da82c3bc5ee" }, - {"chainmakers_NA", "0276c6d1c65abc64c8559710b8aff4b9e33787072d3dda4ec9a47b30da0725f57a" }, - {"chainstrike_SH", "0370bcf10575d8fb0291afad7bf3a76929734f888228bc49e35c5c49b336002153" }, - {"cipi_AR", "02c4f89a5b382750836cb787880d30e23502265054e1c327a5bfce67116d757ce8" }, - {"cipi_NA", "02858904a2a1a0b44df4c937b65ee1f5b66186ab87a751858cf270dee1d5031f18" }, - {"crackers_EU", "03bc819982d3c6feb801ec3b720425b017d9b6ee9a40746b84422cbbf929dc73c3" }, - {"crackers_NA", "03205049103113d48c7c7af811b4c8f194dafc43a50d5313e61a22900fc1805b45" }, // 20 - {"dwy_EU", "0259c646288580221fdf0e92dbeecaee214504fdc8bbdf4a3019d6ec18b7540424" }, - {"emmanux_SH", "033f316114d950497fc1d9348f03770cd420f14f662ab2db6172df44c389a2667a" }, - {"etszombi_EU", "0281b1ad28d238a2b217e0af123ce020b79e91b9b10ad65a7917216eda6fe64bf7" }, - {"fullmoon_AR", "03380314c4f42fa854df8c471618751879f9e8f0ff5dbabda2bd77d0f96cb35676" }, - {"fullmoon_NA", "030216211d8e2a48bae9e5d7eb3a42ca2b7aae8770979a791f883869aea2fa6eef" }, - {"fullmoon_SH", "03f34282fa57ecc7aba8afaf66c30099b5601e98dcbfd0d8a58c86c20d8b692c64" }, - {"goldenman_EU", "02d6f13a8f745921cdb811e32237bb98950af1a5952be7b3d429abd9152f8e388d" }, - {"indenodes_AR", "02ec0fa5a40f47fd4a38ea5c89e375ad0b6ddf4807c99733c9c3dc15fb978ee147" }, - {"indenodes_EU", "0221387ff95c44cb52b86552e3ec118a3c311ca65b75bf807c6c07eaeb1be8303c" }, - {"indenodes_NA", "02698c6f1c9e43b66e82dbb163e8df0e5a2f62f3a7a882ca387d82f86e0b3fa988" }, // 30 - {"indenodes_SH", "0334e6e1ec8285c4b85bd6dae67e17d67d1f20e7328efad17ce6fd24ae97cdd65e" }, - {"jackson_AR", "038ff7cfe34cb13b524e0941d5cf710beca2ffb7e05ddf15ced7d4f14fbb0a6f69" }, - {"jeezy_EU", "023cb3e593fb85c5659688528e9a4f1c4c7f19206edc7e517d20f794ba686fd6d6" }, - {"karasugoi_NA", "02a348b03b9c1a8eac1b56f85c402b041c9bce918833f2ea16d13452309052a982" }, - {"komodoninja_EU", "038e567b99806b200b267b27bbca2abf6a3e8576406df5f872e3b38d30843cd5ba" }, - {"komodoninja_SH", "033178586896915e8456ebf407b1915351a617f46984001790f0cce3d6f3ada5c2" }, - {"komodopioneers_SH", "033ace50aedf8df70035b962a805431363a61cc4e69d99d90726a2d48fb195f68c" }, - {"libscott_SH", "03301a8248d41bc5dc926088a8cf31b65e2daf49eed7eb26af4fb03aae19682b95" }, - {"lukechilds_AR", "031aa66313ee024bbee8c17915cf7d105656d0ace5b4a43a3ab5eae1e14ec02696" }, - {"madmax_AR", "03891555b4a4393d655bf76f0ad0fb74e5159a615b6925907678edc2aac5e06a75" }, // 40 - {"meshbits_AR", "02957fd48ae6cb361b8a28cdb1b8ccf5067ff68eb1f90cba7df5f7934ed8eb4b2c" }, - {"meshbits_SH", "025c6e94877515dfd7b05682b9cc2fe4a49e076efe291e54fcec3add78183c1edb" }, - {"metaphilibert_AR", "02adad675fae12b25fdd0f57250b0caf7f795c43f346153a31fe3e72e7db1d6ac6" }, - {"metaphilibert_SH", "0284af1a5ef01503e6316a2ca4abf8423a794e9fc17ac6846f042b6f4adedc3309" }, - {"patchkez_SH", "0296270f394140640f8fa15684fc11255371abb6b9f253416ea2734e34607799c4" }, - {"pbca26_NA", "0276aca53a058556c485bbb60bdc54b600efe402a8b97f0341a7c04803ce204cb5" }, - {"peer2cloud_AR", "034e5563cb885999ae1530bd66fab728e580016629e8377579493b386bf6cebb15" }, - {"peer2cloud_SH", "03396ac453b3f23e20f30d4793c5b8ab6ded6993242df4f09fd91eb9a4f8aede84" }, - {"polycryptoblog_NA", "02708dcda7c45fb54b78469673c2587bfdd126e381654819c4c23df0e00b679622" }, - {"hyper_AR", "020f2f984d522051bd5247b61b080b4374a7ab389d959408313e8062acad3266b4" }, // 50 - {"hyper_EU", "03d00cf9ceace209c59fb013e112a786ad583d7de5ca45b1e0df3b4023bb14bf51" }, - {"hyper_SH", "0383d0b37f59f4ee5e3e98a47e461c861d49d0d90c80e9e16f7e63686a2dc071f3" }, - {"hyper_NA", "03d91c43230336c0d4b769c9c940145a8c53168bf62e34d1bccd7f6cfc7e5592de" }, - {"popcornbag_AR", "02761f106fb34fbfc5ddcc0c0aa831ed98e462a908550b280a1f7bd32c060c6fa3" }, - {"popcornbag_NA", "03c6085c7fdfff70988fda9b197371f1caf8397f1729a844790e421ee07b3a93e8" }, - {"alien_AR", "0348d9b1fc6acf81290405580f525ee49b4749ed4637b51a28b18caa26543b20f0" }, - {"alien_EU", "020aab8308d4df375a846a9e3b1c7e99597b90497efa021d50bcf1bbba23246527" }, - {"thegaltmines_NA", "031bea28bec98b6380958a493a703ddc3353d7b05eb452109a773eefd15a32e421" }, - {"titomane_AR", "029d19215440d8cb9cc6c6b7a4744ae7fb9fb18d986e371b06aeb34b64845f9325" }, - {"titomane_EU", "0360b4805d885ff596f94312eed3e4e17cb56aa8077c6dd78d905f8de89da9499f" }, // 60 - {"titomane_SH", "03573713c5b20c1e682a2e8c0f8437625b3530f278e705af9b6614de29277a435b" }, - {"webworker01_NA", "03bb7d005e052779b1586f071834c5facbb83470094cff5112f0072b64989f97d7" }, - {"xrobesx_NA", "03f0cc6d142d14a40937f12dbd99dbd9021328f45759e26f1877f2a838876709e1" }, -}; #define CRYPTO777_PUBSECPSTR "020e46e79a2a8d12b9b5d12c7a91adb4e454edfae43c0a0cb805427d2ac7613fd9" +int32_t getkmdseason(int32_t height) +{ + if ( height <= KMD_SEASON_HEIGHTS[0] ) + return(1); + for (int32_t i = 1; i < NUM_KMD_SEASONS; i++) + { + if ( height <= KMD_SEASON_HEIGHTS[i] && height >= KMD_SEASON_HEIGHTS[i-1] ) + return(i+1); + } + return(0); +}; + +int32_t getacseason(uint32_t timestamp) +{ + if ( timestamp <= KMD_SEASON_TIMESTAMPS[0] ) + return(1); + for (int32_t i = 1; i < NUM_KMD_SEASONS; i++) + { + if ( timestamp <= KMD_SEASON_TIMESTAMPS[i] && timestamp >= KMD_SEASON_TIMESTAMPS[i-1] ) + return(i+1); + } + return(0); +}; + int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp) { - static uint8_t elected_pubkeys0[64][33],elected_pubkeys1[64][33],did0,did1; static int32_t n0,n1; int32_t i,htind,n; uint64_t mask = 0; struct knotary_entry *kp,*tmp; - + static uint8_t kmd_pubkeys[NUM_KMD_SEASONS][64][33],didinit[NUM_KMD_SEASONS]; + if ( timestamp == 0 && ASSETCHAINS_SYMBOL[0] != 0 ) timestamp = komodo_heightstamp(height); else if ( ASSETCHAINS_SYMBOL[0] == 0 ) timestamp = 0; // If this chain is not a staked chain, use the normal Komodo logic to determine notaries. This allows KMD to still sync and use its proper pubkeys for dPoW. - if (is_STAKED(ASSETCHAINS_SYMBOL) == 0) + if ( is_STAKED(ASSETCHAINS_SYMBOL) == 0 ) { - if ( height >= KOMODO_NOTARIES_HARDCODED || ASSETCHAINS_SYMBOL[0] != 0 ) - timestamp = 0; - if ( (timestamp != 0 && timestamp <= KOMODO_NOTARIES_TIMESTAMP1) || (ASSETCHAINS_SYMBOL[0] == 0 && height <= KOMODO_NOTARIES_HEIGHT1) ) + int32_t kmd_season = 0; + if ( ASSETCHAINS_SYMBOL[0] == 0 ) { - if ( did0 == 0 ) - { - n0 = (int32_t)(sizeof(Notaries_elected0)/sizeof(*Notaries_elected0)); - for (i=0; i= KOMODO_NOTARIES_HARDCODED ) + kmd_season = getkmdseason(height); } - else //if ( (timestamp != 0 && timestamp <= KOMODO_NOTARIES_TIMESTAMP2) || height <= KOMODO_NOTARIES_HEIGHT2 ) + else { - if ( did1 == 0 ) + // This is a non LABS assetchain, use timestamp to detemine notary pubkeys. + kmd_season = getacseason(timestamp); + } + if ( kmd_season != 0 ) + { + if ( didinit[kmd_season-1] == 0 ) { - n1 = (int32_t)(sizeof(Notaries_elected1)/sizeof(*Notaries_elected1)); - for (i=0; i 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 || (ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0) ) + if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 || (ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0) || KOMODO_SNAPSHOT_INTERVAL != 0 || ASSETCHAINS_EARLYTXIDCONTRACT != 0 ) { fprintf(stderr,"perc %.4f%% ac_pub=[%02x%02x%02x...] acsize.%d\n",dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0],ASSETCHAINS_OVERRIDE_PUBKEY33[1],ASSETCHAINS_OVERRIDE_PUBKEY33[2],(int32_t)ASSETCHAINS_SCRIPTPUB.size()); extraptr = extrabuf; @@ -2072,6 +2076,7 @@ void komodo_args(char *argv0) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS),(void *)&ASSETCHAINS_FOUNDERS); if ( ASSETCHAINS_FOUNDERS_REWARD != 0 ) { + fprintf(stderr, "set founders reward.%li\n",ASSETCHAINS_FOUNDERS_REWARD); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_FOUNDERS_REWARD),(void *)&ASSETCHAINS_FOUNDERS_REWARD); } } @@ -2147,6 +2152,14 @@ void komodo_args(char *argv0) extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_NK[0]),(void *)&ASSETCHAINS_NK[0]); extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_NK[1]),(void *)&ASSETCHAINS_NK[1]); } + if ( KOMODO_SNAPSHOT_INTERVAL != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(KOMODO_SNAPSHOT_INTERVAL),(void *)&KOMODO_SNAPSHOT_INTERVAL); + } + if ( ASSETCHAINS_EARLYTXIDCONTRACT != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_EARLYTXIDCONTRACT),(void *)&ASSETCHAINS_EARLYTXIDCONTRACT); + } } addn = GetArg("-seednode",""); @@ -2214,7 +2227,10 @@ void komodo_args(char *argv0) sprintf(fname,"%s_7776",ASSETCHAINS_SYMBOL); if ( (fp= fopen(fname,"wb")) != 0 ) { - fprintf(fp,iguanafmtstr,name.c_str(),name.c_str(),name.c_str(),name.c_str(),magicstr,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,"78.47.196.146"); + int8_t notarypay = 0; + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) + notarypay = 1; + fprintf(fp,iguanafmtstr,name.c_str(),name.c_str(),name.c_str(),name.c_str(),magicstr,ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT,"78.47.196.146",notarypay); fclose(fp); //printf("created (%s)\n",fname); } else printf("error creating (%s)\n",fname); diff --git a/src/main.cpp b/src/main.cpp index d617f308a..9f837a016 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -642,6 +642,106 @@ UniValue komodo_snapshot(int top) return(result); } +bool komodo_snapshot2(std::map &addressAmounts) +{ + if ( fAddressIndex && pblocktree != 0 ) + { + return pblocktree->Snapshot2(addressAmounts, 0); + } + else return false; +} + +int32_t lastSnapShotHeight = 0; +std::vector > vAddressSnapshot; + +bool komodo_dailysnapshot(int32_t height) +{ + int reorglimit = 100; + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height,undo_height,extraoffset; + // NOTE: To make this 100% safe under all sync conditions, it should be using a notarized notarization, from the DB. + // Under heavy reorg attack, its possible `komodo_notarized_height` can return a height that can't be found on chain sync. + // However, the DB can reorg the last notarization. By using 2 deep, we know 100% that the previous notarization cannot be reorged by online nodes, + // and as such will always be notarizing the same height. May need to check heights on scan back to make sure they are confirmed in correct order. + if ( (extraoffset= height % KOMODO_SNAPSHOT_INTERVAL) != 0 ) + { + // we are on chain init, and need to scan all the way back to the correct height, other wise our node will have a diffrent snapshot to online nodes. + // use the notarizationsDB to scan back from the consesnus height to get the offset we need. + std::string symbol; Notarisation nota; + symbol.assign(ASSETCHAINS_SYMBOL); + if ( ScanNotarisationsDB(height-extraoffset, symbol, 100, nota) == 0 ) + undo_height = height-extraoffset-reorglimit; + else undo_height = nota.second.height; + //fprintf(stderr, "height.%i-extraoffset.%i = startscanfrom.%i to get undo_height.%i\n", height, extraoffset, height-extraoffset, undo_height); + } + else + { + // we are at the right height in connect block to scan back to last notarized height. + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + notarized_height > height-reorglimit ? undo_height = notarized_height : undo_height = height-reorglimit; + } + fprintf(stderr, "doing snapshot for height.%i undo_height.%i\n", height, undo_height); + // if we already did this height dont bother doing it again, this is just a reorg. The actual snapshot height cannot be reorged. + if ( undo_height == lastSnapShotHeight ) + return true; + std::map addressAmounts; + if ( !komodo_snapshot2(addressAmounts) ) + return false; + + // undo blocks in reverse order + for (int32_t n = height; n > undo_height; n--) + { + //fprintf(stderr, "undoing block.%i\n",n); + CBlockIndex *pindex; CBlock block; + if ( (pindex= komodo_chainactive(n)) == 0 || komodo_blockload(block, pindex) != 0 ) + return false; + // undo transactions in reverse order + for (int32_t i = block.vtx.size() - 1; i >= 0; i--) + { + const CTransaction &tx = block.vtx[i]; + CTxDestination vDest; + // loop vouts reverse order, remove value recieved. + for (unsigned int k = tx.vout.size(); k-- > 0;) + { + const CTxOut &out = tx.vout[k]; + if ( ExtractDestination(out.scriptPubKey, vDest) ) + { + addressAmounts[CBitcoinAddress(vDest).ToString()] -= out.nValue; + if ( addressAmounts[CBitcoinAddress(vDest).ToString()] < 1 ) + addressAmounts.erase(CBitcoinAddress(vDest).ToString()); + //fprintf(stderr, "VOUT: address.%s remove_coins.%li\n",CBitcoinAddress(vDest).ToString().c_str(), out.nValue); + } + } + // loop vins in reverse order, get prevout and return the sent balance. + for (unsigned int j = tx.vin.size(); j-- > 0;) + { + uint256 blockhash; CTransaction txin; + if ( !tx.IsCoinImport() && !tx.IsCoinBase() && myGetTransaction(tx.vin[j].prevout.hash,txin,blockhash) ) + { + int vout = tx.vin[j].prevout.n; + if ( ExtractDestination(txin.vout[vout].scriptPubKey, vDest) ) + { + //fprintf(stderr, "VIN: address.%s add_coins.%li\n",CBitcoinAddress(vDest).ToString().c_str(), txin.vout[vout].nValue); + addressAmounts[CBitcoinAddress(vDest).ToString()] += txin.vout[vout].nValue; + } + } + } + } + } + vAddressSnapshot.clear(); // clear existing snapshot + // convert address string to destination for easier conversion to what ever is required, eg, scriptPubKey. + for ( auto element : addressAmounts) + vAddressSnapshot.push_back(make_pair(element.second, DecodeDestination(element.first))); + // sort the vector by amount, highest at top. + std::sort(vAddressSnapshot.rbegin(), vAddressSnapshot.rend()); + //for (int j = 0; j < 50; j++) + // fprintf(stderr, "j.%i address.%s nValue.%li\n",j, CBitcoinAddress(vAddressSnapshot[j].second).ToString().c_str(), vAddressSnapshot[j].first ); + // include only top 3999 address. + if ( vAddressSnapshot.size() > 3999 ) vAddressSnapshot.resize(3999); + lastSnapShotHeight = undo_height; + fprintf(stderr, "vAddressSnapshot.size.%li\n", vAddressSnapshot.size()); + return true; +} + ////////////////////////////////////////////////////////////////////////////// // // mapOrphanTransactions @@ -1273,25 +1373,24 @@ bool CheckTransaction(uint32_t tiptime,const CTransaction& tx, CValidationState } } -int32_t komodo_isnotaryvout(char *coinaddr) // from ac_private chains only +int32_t komodo_isnotaryvout(char *coinaddr,uint32_t tiptime) // from ac_private chains only { - static int32_t didinit; static char notaryaddrs[sizeof(Notaries_elected1)/sizeof(*Notaries_elected1) + 1][64]; - int32_t i; - if ( didinit == 0 ) + int32_t season = getacseason(tiptime); + if ( NOTARY_ADDRESSES[season-1][0][0] == 0 ) { - uint8_t pubkey33[33]; - for (i=0; i<=sizeof(Notaries_elected1)/sizeof(*Notaries_elected1); i++) - { - if ( i < sizeof(Notaries_elected1)/sizeof(*Notaries_elected1) ) - decode_hex(pubkey33,33,(char *)Notaries_elected1[i][1]); - else decode_hex(pubkey33,33,(char *)CRYPTO777_PUBSECPSTR); - pubkey2addr((char *)notaryaddrs[i],(uint8_t *)pubkey33); - } - didinit = 1; + uint8_t pubkeys[64][33]; + komodo_notaries(pubkeys,0,tiptime); } - for (i=0; i<=sizeof(Notaries_elected1)/sizeof(*Notaries_elected1); i++) - if ( strcmp(coinaddr,notaryaddrs[i]) == 0 ) + if ( strcmp(coinaddr,CRYPTO777_KMDADDR) == 0 ) return(1); + for (int32_t i = 0; i < NUM_KMD_NOTARIES; i++) + { + if ( strcmp(coinaddr,NOTARY_ADDRESSES[season-1][i]) == 0 ) + { + //fprintf(stderr, "coinaddr.%s notaryaddress[%i].%s\n",coinaddr,i,NOTARY_ADDRESSES[season-1][i]); + return(1); + } + } return(0); } @@ -1384,7 +1483,7 @@ bool CheckTransactionWithoutProofVerification(uint32_t tiptime,const CTransactio // char destaddr[65]; Getscriptaddress(destaddr,txout.scriptPubKey); - if ( komodo_isnotaryvout(destaddr) == 0 ) + if ( komodo_isnotaryvout(destaddr,tiptime) == 0 ) { invalid_private_taddr = 1; //return state.DoS(100, error("CheckTransaction(): this is a private chain, no public allowed"),REJECT_INVALID, "bad-txns-acprivacy-chain"); @@ -3003,7 +3102,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex *pfClean = false; bool fClean = true; - komodo_disconnect(pindex,block); + //komodo_disconnect(pindex,block); does nothing? CBlockUndo blockUndo; CDiskBlockPos pos = pindex->GetUndoPos(); if (pos.IsNull()) @@ -3096,7 +3195,6 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex uint160 addrHash = addr.size() == 20 ? uint160(addr) : Hash160(addr); // undo spending activity addressIndex.push_back(make_pair(CAddressIndexKey(keyType, addrHash, pindex->GetHeight(), i, hash, j, true), prevout.nValue * -1)); - // restore unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(keyType, addrHash, input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); } @@ -3269,7 +3367,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin auto verifier = libzcash::ProofVerifier::Strict(); auto disabledVerifier = libzcash::ProofVerifier::Disabled(); int32_t futureblock; - CAmount blockReward = 0; + CAmount blockReward = 0; uint64_t notarypaycheque = 0; // Check it again to verify JoinSplit proofs, and in case a previous version let a bad block in if (!CheckBlock(&futureblock,pindex->GetHeight(),pindex,block, state, fExpensiveChecks ? verifier : disabledVerifier, fCheckPOW, !fJustCheck) || futureblock != 0 ) { @@ -3292,7 +3390,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin { // do a full block scan to get notarisation position and to enforce a valid notarization is in position 1. // if notarisation in the block, must be position 1 and the coinbase must pay notaries. - int notarisationTx = komodo_connectblock(true,pindex,*(CBlock *)&block); + int32_t notarisationTx = komodo_connectblock(true,pindex,*(CBlock *)&block); // -1 means that the valid notarization isnt in position 1 or there are too many notarizations in this block. if ( notarisationTx == -1 ) return state.DoS(100, error("ConnectBlock(): Notarization is not in TX position 1 or block contains more than 1 notarization! Invalid Block!"), @@ -3307,7 +3405,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): Notaries have not been paid!"), REJECT_INVALID, "bad-cb-amount"); // calculate the notaries compensation and validate the amounts and pubkeys are correct. - uint64_t notarypaycheque = komodo_checknotarypay((CBlock *)&block,(int32_t)pindex->GetHeight()); + notarypaycheque = komodo_checknotarypay((CBlock *)&block,(int32_t)pindex->GetHeight()); //fprintf(stderr, "notarypaycheque.%lu\n", notarypaycheque); if ( notarypaycheque > 0 ) blockReward += notarypaycheque; @@ -3497,8 +3595,8 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin fprintf(stderr,"valueout %.8f too big\n",(double)valueout/COIN); return state.DoS(100, error("ConnectBlock(): GetValueOut too big"),REJECT_INVALID,"tx valueout is too big"); } - prevsum = voutsum; - voutsum += valueout; + //prevsum = voutsum; + //voutsum += valueout; /*if ( KOMODO_VALUETOOBIG(voutsum) != 0 ) { fprintf(stderr,"voutsum %.8f too big\n",(double)voutsum/COIN); @@ -3714,6 +3812,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin //FlushStateToDisk(); komodo_connectblock(false,pindex,*(CBlock *)&block); // dPoW state update. + if ( ASSETCHAINS_NOTARY_PAY[0] != 0 ) + { + // Update the notary pay with the latest payment. + pindex->nNotaryPay = pindex->pprev->nNotaryPay + notarypaycheque; + //fprintf(stderr, "total notary pay.%li\n", pindex->nNotaryPay); + } return true; } @@ -3923,6 +4027,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { DisconnectNotarisations(block); } pindexDelete->segid = -2; + pindexDelete->nNotaryPay = 0; pindexDelete->newcoins = 0; pindexDelete->zfunds = 0; @@ -3943,7 +4048,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { CValidationState stateDummy; // don't keep staking or invalid transactions - if (tx.IsCoinBase() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight()) != 0)) || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) + if (tx.IsCoinBase() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight(),true) != 0)) || !AcceptToMemoryPool(mempool, stateDummy, tx, false, NULL)) { mempool.remove(tx, removed, true); } @@ -3975,11 +4080,11 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { { CTransaction &tx = block.vtx[i]; //if ((i == (block.vtx.size() - 1)) && ((ASSETCHAINS_LWMAPOS && block.IsVerusPOSBlock()) || (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block) != 0)))) - if ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight()) != 0))) + if ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED != 0 && (komodo_isPoS((CBlock *)&block,pindexDelete->GetHeight(),true) != 0))) { #ifdef ENABLE_WALLET - LOCK2(cs_main, pwalletMain->cs_wallet); - pwalletMain->EraseFromWallet(tx.GetHash()); + if ( !GetBoolArg("-disablewallet", false) ) + pwalletMain->EraseFromWallet(tx.GetHash()); #endif } else @@ -4156,6 +4261,16 @@ bool static ConnectTip(CValidationState &state, CBlockIndex *pindexNew, CBlock * komodo_pricesupdate(pindexNew->GetHeight(),pblock); if ( ASSETCHAINS_SAPLING <= 0 && pindexNew->nTime > KOMODO_SAPLING_ACTIVATION - 24*3600 ) komodo_activate_sapling(pindexNew); + if ( ASSETCHAINS_CC != 0 && KOMODO_SNAPSHOT_INTERVAL != 0 && (pindexNew->GetHeight() % KOMODO_SNAPSHOT_INTERVAL) == 0 && pindexNew->GetHeight() >= KOMODO_SNAPSHOT_INTERVAL ) + { + uint64_t start = time(NULL); + if ( !komodo_dailysnapshot(pindexNew->GetHeight()) ) + { + fprintf(stderr, "daily snapshot failed, please reindex your chain\n"); + StartShutdown(); + } + fprintf(stderr, "snapshot completed in: %lu seconds\n", time(NULL)-start); + } return true; } @@ -4980,7 +5095,7 @@ bool CheckBlock(int32_t *futureblockp,int32_t height,CBlockIndex *pindex,const C CValidationState state; CTransaction Tx; const CTransaction &tx = (CTransaction)block.vtx[i]; - if (tx.IsCoinBase() || !tx.vjoinsplit.empty() || !tx.vShieldedSpend.empty() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block,height) != 0))) + if (tx.IsCoinBase() || !tx.vjoinsplit.empty() || !tx.vShieldedSpend.empty() || ((i == (block.vtx.size() - 1)) && (ASSETCHAINS_STAKED && komodo_isPoS((CBlock *)&block,height,true) != 0))) continue; Tx = tx; if ( myAddtomempool(Tx, &state, true) == false ) // happens with out of order tx in block on resync @@ -6095,7 +6210,7 @@ bool CVerifyDB::VerifyDB(CCoinsView *coinsview, int nCheckLevel, int nCheckDepth } LogPrintf("No coin database inconsistencies in last %i blocks (%i transactions)\n", chainActive.Height() - pindexState->GetHeight(), nGoodTransactions); - + return true; } @@ -6360,7 +6475,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) try { // This takes over fileIn and calls fclose() on it in the CBufferedFile destructor //CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE, MAX_BLOCK_SIZE+8, SER_DISK, CLIENT_VERSION); - CBufferedFile blkdat(fileIn, 32*MAX_BLOCK_SIZE(10000000), MAX_BLOCK_SIZE(10000000)+8, SER_DISK, CLIENT_VERSION); + CBufferedFile blkdat(fileIn, 2*MAX_BLOCK_SIZE(10000000), MAX_BLOCK_SIZE(10000000)+8, SER_DISK, CLIENT_VERSION); uint64_t nRewind = blkdat.GetPos(); while (!blkdat.eof()) { boost::this_thread::interruption_point(); @@ -6387,15 +6502,15 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) } try { // read block + CBlock block; uint64_t nBlockPos = blkdat.GetPos(); if (dbp) dbp->nPos = nBlockPos; blkdat.SetLimit(nBlockPos + nSize); blkdat.SetPos(nBlockPos); - CBlock block; blkdat >> block; + nRewind = blkdat.GetPos(); - // detect out of order blocks, and store them for later uint256 hash = block.GetHash(); if (hash != chainparams.GetConsensus().hashGenesisBlock && mapBlockIndex.find(block.hashPrevBlock) == mapBlockIndex.end()) { @@ -6426,6 +6541,7 @@ bool LoadExternalBlockFile(FILE* fileIn, CDiskBlockPos *dbp) std::pair::iterator, std::multimap::iterator> range = mapBlocksUnknownParent.equal_range(head); while (range.first != range.second) { std::multimap::iterator it = range.first; + if (ReadBlockFromDisk(mapBlockIndex.count(hash)!=0?mapBlockIndex[hash]->GetHeight():0,block, it->second,1)) { LogPrintf("%s: Processing out of order child %s of %s\n", __func__, block.GetHash().ToString(), diff --git a/src/main.h b/src/main.h index ec36c976b..d507f9dd6 100644 --- a/src/main.h +++ b/src/main.h @@ -88,7 +88,7 @@ static const unsigned int DEFAULT_MIN_RELAY_TX_FEE = 100; /** Default for -maxorphantx, maximum number of orphan transactions kept in memory */ static const unsigned int DEFAULT_MAX_ORPHAN_TRANSACTIONS = 100; /** Default for -txexpirydelta, in number of blocks */ -static const unsigned int DEFAULT_TX_EXPIRY_DELTA = 20; +static const unsigned int DEFAULT_TX_EXPIRY_DELTA = 200; /** The maximum size of a blk?????.dat file (since 0.8) */ static const unsigned int MAX_BLOCKFILE_SIZE = 0x8000000; // 128 MiB static const unsigned int MAX_TEMPFILE_SIZE = 0x1000000; // 16 MiB 0x8000000 diff --git a/src/miner.cpp b/src/miner.cpp index e199acccd..9976909fa 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -156,6 +156,7 @@ uint64_t komodo_notarypay(CMutableTransaction &txNew, std::vector &Notar int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *script, int32_t len); CScript komodo_mineropret(int32_t nHeight); +bool komodo_appendACscriptpub(); CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32_t gpucount, bool isStake) { @@ -290,9 +291,9 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txvalue = tx.GetValueOut(); if ( KOMODO_VALUETOOBIG(txvalue) != 0 ) continue; - if ( KOMODO_VALUETOOBIG(txvalue + voutsum) != 0 ) - continue; - voutsum += txvalue; + //if ( KOMODO_VALUETOOBIG(txvalue + voutsum) != 0 ) // has been commented from main.cpp ? + // continue; + //voutsum += txvalue; if ( ASSETCHAINS_SYMBOL[0] == 0 && komodo_validate_interest(tx,nHeight,(uint32_t)pblock->nTime,0) < 0 ) { //fprintf(stderr,"CreateNewBlock: komodo_validate_interest failure nHeight.%d nTime.%u vs locktime.%u\n",nHeight,(uint32_t)pblock->nTime,(uint32_t)tx.nLockTime); @@ -652,6 +653,12 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 txNew.vout[1].nValue = commission; if ( ASSETCHAINS_SCRIPTPUB.size() > 1 ) { + static bool didinit = false; + if ( !didinit && nHeight > 100 && KOMODO_EARLYTXID != zeroid && komodo_appendACscriptpub() ) + { + fprintf(stderr, "appended ccopreturn to ASSETCHAINS_SCRIPTPUB.%s\n", ASSETCHAINS_SCRIPTPUB.c_str()); + didinit = true; + } //fprintf(stderr,"mine to -ac_script\n"); //txNew.vout[1].scriptPubKey = CScript() << ParseHex(); int32_t len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); @@ -721,7 +728,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 } return(0); } - fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats); + //fprintf(stderr, "Created notary payment coinbase totalsat.%lu\n",totalsats); } else fprintf(stderr, "vout 2 of notarisation is not OP_RETURN scriptlen.%i\n", scriptlen); } if ( ASSETCHAINS_CBOPRET != 0 ) @@ -803,12 +810,17 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32 //fprintf(stderr,"check validity\n"); if ( !TestBlockValidity(state, *pblock, pindexPrev, false, false)) // invokes CC checks { + if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) + { + LEAVE_CRITICAL_SECTION(cs_main); + LEAVE_CRITICAL_SECTION(mempool.cs); + } throw std::runtime_error("CreateNewBlock(): TestBlockValidity failed"); } //fprintf(stderr,"valid\n"); } } - if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) + if ( ASSETCHAINS_SYMBOL[0] == 0 || (ASSETCHAINS_SYMBOL[0] != 0 && !isStake) ) { LEAVE_CRITICAL_SECTION(cs_main); LEAVE_CRITICAL_SECTION(mempool.cs); @@ -895,14 +907,14 @@ void IncrementExtraNonce(CBlock* pblock, CBlockIndex* pindexPrev, unsigned int& CBlockTemplate* CreateNewBlockWithKey(CReserveKey& reservekey, int32_t nHeight, int32_t gpucount, bool isStake) { CPubKey pubkey; CScript scriptPubKey; uint8_t *script,*ptr; int32_t i,len; - if ( nHeight == 1 && ASSETCHAINS_COMMISSION != 0 ) + if ( nHeight == 1 && ASSETCHAINS_COMMISSION != 0 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()] != 49 && ASSETCHAINS_SCRIPTPUB[ASSETCHAINS_SCRIPTPUB.back()-1] != 51 ) { if ( ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 ) { pubkey = ParseHex(ASSETCHAINS_OVERRIDE_PUBKEY); scriptPubKey = CScript() << ParseHex(HexStr(pubkey)) << OP_CHECKSIG; } - else + else { len = strlen(ASSETCHAINS_SCRIPTPUB.c_str()); len >>= 1; @@ -1044,7 +1056,7 @@ int32_t roundrobin_delay; arith_uint256 HASHTarget,HASHTarget_POW; // wait for peers to connect -int32_t waitForPeers(const CChainParams &chainparams) +void waitForPeers(const CChainParams &chainparams) { if (chainparams.MiningRequiresPeers()) { diff --git a/src/notaries_staked.cpp b/src/notaries_staked.cpp index 0b7c97ceb..d3281127e 100644 --- a/src/notaries_staked.cpp +++ b/src/notaries_staked.cpp @@ -18,7 +18,7 @@ int8_t is_STAKED(const char *chain_name) if (doneinit == 1 && ASSETCHAINS_SYMBOL[0] != 0) return(STAKED); else STAKED = 0; - if ( (strcmp(chain_name, "LABS") == 0) || (strcmp(chain_name, "LABSTH") == 0) ) + if ( (strcmp(chain_name, "LABS") == 0) ) STAKED = 1; // These chains are allowed coin emissions. else if ( (strncmp(chain_name, "LABS", 4) == 0) ) STAKED = 2; // These chains have no coin emission, block subsidy is always 0, and comission is 0. Notary pay is allowed. @@ -113,7 +113,14 @@ void UpdateNotaryAddrs(uint8_t pubkeys[64][33],int8_t numNotaries) { // staked era is set. pthread_mutex_lock(&staked_mutex); for (int i = 0; i +#include "cc/CCinclude.h" +#include "cc/CCPrices.h" + using namespace std; +extern int32_t KOMODO_INSYNC; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex); +int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); #include "komodo_defs.h" #include "komodo_structs.h" @@ -129,6 +134,9 @@ UniValue blockheaderToJSON(const CBlockIndex* blockindex) result.push_back(Pair("error", "null blockhash")); return(result); } + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + result.push_back(Pair("last_notarized_height", notarized_height)); result.push_back(Pair("hash", blockindex->GetBlockHash().GetHex())); int confirmations = -1; // Only report confirmations if the block is on the main chain @@ -280,6 +288,9 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) UniValue blockToJSON(const CBlock& block, const CBlockIndex* blockindex, bool txDetails = false) { UniValue result(UniValue::VOBJ); + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); + result.push_back(Pair("last_notarized_height", notarized_height)); result.push_back(Pair("hash", block.GetHash().GetHex())); int confirmations = -1; // Only report confirmations if the block is on the main chain @@ -1155,17 +1166,17 @@ UniValue paxprice(const UniValue& params, bool fHelp) } return ret; } - +// fills pricedata with raw price, correlated and smoothed values for numblock /*int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) { int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,ignore,rngval; uint32_t rawprices[1440*6],*ptr; int64_t *tmpbuf; - width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; + width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; // need 2*PRICES_DAYWINDOW previous raw price points to calc PRICES_DAYWINDOW correlated points to calc, in turn, smoothed point komodo_heightpricebits(&seed,rawprices,firstheight + numblocks - 1); if ( firstheight < width ) return(-1); for (i=0; i vexpr; + SplitStr(sexpr, vexpr); + + // debug print parsed strings: + std::cerr << "parsed synthetic: "; + for (auto s : vexpr) + std::cerr << s << " "; + std::cerr << std::endl; + + return PricesBet(txfee, amount, leverage, vexpr); +} + +// pricesaddfunding rpc implementation +UniValue pricesaddfunding(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesaddfunding bettxid amount\n" + "where amount is in coins\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + CAmount txfee = 10000; + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + CAmount amount = atof(params[1].get_str().c_str()) * COIN; + if (amount <= 0) + throw runtime_error("invalid amount\n"); + + return PricesAddFunding(txfee, bettxid, amount); +} + +// rpc pricessetcostbasis implementation +UniValue pricessetcostbasis(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricessetcostbasis bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesSetcostbasis(txfee, bettxid); +} + +// pricescashout rpc implementation +UniValue pricescashout(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricescashout bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesCashout(txfee, bettxid); +} + +// pricesrekt rpc implementation +UniValue pricesrekt(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesrekt bettxid height\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int32_t height = atoi(params[0].get_str().c_str()); + + int64_t txfee = 10000; + + return PricesRekt(txfee, bettxid, height); +} + +// pricesrekt rpc implementation +UniValue pricesgetorderbook(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error("pricesgetorderbook\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + return PricesGetOrderbook(); +} + +// pricesrekt rpc implementation +UniValue pricesrefillfund(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricesrefillfund amount\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + CAmount amount = atof(params[0].get_str().c_str()) * COIN; + + return PricesRefillFund(amount); +} + + UniValue gettxout(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) @@ -1551,6 +1714,7 @@ UniValue getblockchaininfo(const UniValue& params, bool fHelp) UniValue obj(UniValue::VOBJ); obj.push_back(Pair("chain", Params().NetworkIDString())); obj.push_back(Pair("blocks", (int)chainActive.Height())); + obj.push_back(Pair("synced", KOMODO_INSYNC!=0)); obj.push_back(Pair("headers", pindexBestHeader ? pindexBestHeader->GetHeight() : -1)); obj.push_back(Pair("bestblockhash", chainActive.LastTip()->GetBlockHash().GetHex())); obj.push_back(Pair("difficulty", (double)GetNetworkDifficulty())); diff --git a/src/rpc/crosschain.cpp b/src/rpc/crosschain.cpp index 2ac53f788..bf0dbea4c 100644 --- a/src/rpc/crosschain.cpp +++ b/src/rpc/crosschain.cpp @@ -300,6 +300,9 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp) uint256 tokenid = zeroid; if( params.size() == 4 ) tokenid = Parseuint256(params[3].get_str().c_str()); + + if ( tokenid != zeroid && strcmp("LABS", targetSymbol.c_str())) + throw JSONRPCError(RPC_TYPE_ERROR, "There is no tokens support on LABS."); CPubKey myPubKey = Mypubkey(); struct CCcontract_info *cpTokens, C; @@ -312,8 +315,8 @@ UniValue migrate_createburntransaction(const UniValue& params, bool fHelp) if (tokenid.IsNull()) { // coins int64_t inputs; - if ((inputs = AddNormalinputs(mtx, myPubKey, burnAmount + txfee, 60)) == 0) { - throw runtime_error("Cannot find normal inputs\n"); + if ((inputs = AddNormalinputs(mtx, myPubKey, burnAmount + txfee, 10)) == 0) { + throw runtime_error("not enough funds, or need to merge utxos first\n"); } CTxDestination txdest = DecodeDestination(dest_addr_or_pubkey.c_str()); @@ -1145,36 +1148,43 @@ UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp) //out.push_back(make_pair("blocktime",(int))); UniValue labs(UniValue::VARR); UniValue kmd(UniValue::VARR); - // Gets KMD notaries on KMD... but LABS notaries on labs chains needs to be fixed so LABS are identified on KMD. - int8_t numNN = 0; uint8_t notarypubkeys[64][33] = {0}; + int8_t numNN = 0, numSN = 0; uint8_t notarypubkeys[64][33] = {0}; uint8_t LABSpubkeys[64][33] = {0}; numNN = komodo_notaries(notarypubkeys, height, chainActive[height]->nTime); - + numSN = numStakedNotaries(LABSpubkeys,STAKED_era(chainActive[height]->nTime)); + BOOST_FOREACH(const Notarisation& n, nibs) { UniValue item(UniValue::VOBJ); UniValue notaryarr(UniValue::VARR); std::vector NotarisationNotaries; - if ( is_STAKED(n.second.symbol) != 0 ) - continue; // for now just skip this... need to fetch diff pubkeys for these chains. labs.push_back(item); uint256 hash; CTransaction tx; if ( GetTransaction(n.first,tx,hash,false) ) { - if ( !GetNotarisationNotaries(notarypubkeys, numNN, tx.vin, NotarisationNotaries) ) - continue; - if ( NotarisationNotaries.size() < numNN/5 ) - continue; + if ( is_STAKED(n.second.symbol) != 0 ) + { + if ( !GetNotarisationNotaries(LABSpubkeys, numSN, tx.vin, NotarisationNotaries) ) + continue; + } + else + { + if ( !GetNotarisationNotaries(notarypubkeys, numNN, tx.vin, NotarisationNotaries) ) + continue; + } } item.push_back(make_pair("txid", n.first.GetHex())); item.push_back(make_pair("chain", n.second.symbol)); item.push_back(make_pair("height", (int)n.second.height)); item.push_back(make_pair("blockhash", n.second.blockHash.GetHex())); - item.push_back(make_pair("KMD_height", height)); // for when timstamp input is used. + //item.push_back(make_pair("KMD_height", height)); // for when timstamp input is used. for ( auto notary : NotarisationNotaries ) notaryarr.push_back(notary); item.push_back(make_pair("notaries",notaryarr)); - kmd.push_back(item); + if ( is_STAKED(n.second.symbol) != 0 ) + labs.push_back(item); + else + kmd.push_back(item); } out.push_back(make_pair("KMD", kmd)); - //out.push_back(make_pair("LABS", labs)); + out.push_back(make_pair("LABS", labs)); return out; } @@ -1308,7 +1318,7 @@ UniValue getimports(const UniValue& params, bool fHelp) UniValue objTx(UniValue::VOBJ); objTx.push_back(Pair("txid",tx.GetHash().ToString())); ImportProof proof; CTransaction burnTx; std::vector payouts; CTxDestination importaddress; - TotalImported += tx.vout[1].nValue; + TotalImported += tx.vout[0].nValue; // were vouts swapped? objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[1].nValue))); if (ExtractDestination(tx.vout[1].scriptPubKey, importaddress)) { @@ -1410,6 +1420,7 @@ UniValue getwalletburntransactions(const UniValue& params, bool fHelp) UnmarshalBurnTx(*pwtx, targetSymbol, &targetCCid, payoutsHash, rawproof)) { UniValue entry(UniValue::VOBJ); entry.push_back(Pair("txid", pwtx->GetHash().GetHex())); + if (vopret.begin()[0] == EVAL_TOKENS) { // get burned token value std::vector> oprets; @@ -1450,6 +1461,12 @@ UniValue getwalletburntransactions(const UniValue& params, bool fHelp) } else entry.push_back(Pair("burnedAmount", ValueFromAmount(pwtx->vout.back().nValue))); // coins + + // check for corrupted strings (look for non-printable chars) from some older versions + // which caused "couldn't parse reply from server" error on client: + if (std::find_if(targetSymbol.begin(), targetSymbol.end(), [](int c) {return !std::isprint(c);}) != targetSymbol.end()) + targetSymbol = ""; + entry.push_back(Pair("targetSymbol", targetSymbol)); entry.push_back(Pair("targetCCid", std::to_string(targetCCid))); if (mytxid_inmempool(pwtx->GetHash())) @@ -1481,4 +1498,4 @@ UniValue getwalletburntransactions(const UniValue& params, bool fHelp) ret.push_backV(arrTmp); return ret; -} \ No newline at end of file +} diff --git a/src/rpc/mining.cpp b/src/rpc/mining.cpp index 21ae07810..53a744497 100644 --- a/src/rpc/mining.cpp +++ b/src/rpc/mining.cpp @@ -60,7 +60,8 @@ arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t he * or over the difficulty averaging window if 'lookup' is nonpositive. * If 'height' is nonnegative, compute the estimate at the time when a given block was found. */ -int64_t GetNetworkHashPS(int lookup, int height) { +int64_t GetNetworkHashPS(int lookup, int height) +{ CBlockIndex *pb = chainActive.LastTip(); if (height >= 0 && height < chainActive.Height()) @@ -1051,6 +1052,7 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp) return result; } + static const CRPCCommand commands[] = { // category name actor (function) okSafeMode // --------------------- ------------------------ ----------------------- ---------- diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp index 4e21ab9e9..eca4abb36 100644 --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -69,12 +69,14 @@ bool komodo_txnotarizedconfirmed(uint256 txid); uint32_t komodo_chainactive_timestamp(); int32_t komodo_whoami(char *pubkeystr,int32_t height,uint32_t timestamp); extern uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; -extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN,IS_STAKED_NOTARY,IS_KOMODO_NOTARY,STAKED_ERA; +extern int32_t KOMODO_LASTMINED,JUMBLR_PAUSE,KOMODO_LONGESTCHAIN,IS_STAKED_NOTARY,IS_KOMODO_NOTARY,STAKED_ERA,KOMODO_INSYNC; extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; uint32_t komodo_segid32(char *coinaddr); int64_t komodo_coinsupply(int64_t *zfundsp,int64_t *sproutfundsp,int32_t height); int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp); int8_t StakedNotaryID(std::string ¬aryname, char *Raddress); +uint64_t komodo_notarypayamount(int32_t nHeight, int64_t notarycount); +int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); #define KOMODO_VERSION "0.3.3b" #define VERUS_VERSION "0.4.0g" @@ -168,7 +170,7 @@ UniValue geterablockheights(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 0) throw runtime_error( - "getnotarysendmany\n" + "geterablockheights\n" "Returns a JSON object with the first block in each era.\n" ); @@ -235,6 +237,7 @@ UniValue getinfo(const UniValue& params, bool fHelp) obj.push_back(Pair("version", CLIENT_VERSION)); obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); obj.push_back(Pair("KMDversion", KOMODO_VERSION)); + obj.push_back(Pair("synced", KOMODO_INSYNC!=0)); //obj.push_back(Pair("VRSCversion", VERUS_VERSION)); obj.push_back(Pair("notarized", notarized_height)); obj.push_back(Pair("prevMoMheight", prevMoMheight)); @@ -1216,6 +1219,106 @@ UniValue getaddressdeltas(const UniValue& params, bool fHelp) } } +CAmount checkburnaddress(CAmount &received, int64_t &nNotaryPay, int32_t &height, std::string sAddress) +{ + CBitcoinAddress address(sAddress); + uint160 hashBytes; int type = 0; CAmount balance = 0; + if (address.GetIndexKey(hashBytes, type, false)) + { + std::vector > addressIndex; + if (GetAddressIndex(hashBytes, type, addressIndex)) + { + for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) + { + if (it->second > 0) + received += it->second; + balance += it->second; + } + // Get notary pay from current chain tip + CBlockIndex* pindex = chainActive.LastTip(); + nNotaryPay = pindex->nNotaryPay; + height = pindex->GetHeight(); + } + } + return balance; +} + +UniValue checknotarization(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "checknotarization\n" + "\nReturns true if burn address balance is greater than total notary pay. (requires addressindex to be enabled).\n" + ); + + UniValue result(UniValue::VOBJ); CAmount balance = 0, received = 0; int64_t nNotaryPay = 0; int32_t height; + + // helper to test burn address's + /*uint8_t priv[32] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + uint8_t pub[33] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + char coinaddr[64]; uint8_t buf33[33]; + //pubkey2addr(coinaddr, pub); + priv2addr(coinaddr,buf33,priv); + fprintf(stderr, "what.%s\n", coinaddr); + result.push_back(Pair("address", coinaddr)); + return result; + */ + + if ( ASSETCHAINS_NOTARY_PAY[0] == 0 ) + throw runtime_error("only works for ac_notarypay chains"); + // pubkey 020000000000000000000000000000000 + balance = checkburnaddress(received, nNotaryPay, height, "REDVp3ox1pbcWYCzySadfHhk8UU3HM4k5x"); + if ( nNotaryPay >= balance || received != balance ) + return false; + return true; +} + +UniValue getnotarypayinfo(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error( + "getnotarypayinfo\n" + "\nReturns infomation about ac_notaypay status (requires addressindex to be enabled).\n" + "\nResult:\n" + "{\n" + " \"height\" (number) The current block height\n" + " \"balance\" (number) The current balance of the burn address\n" + " \"spent\" (bool) true if coins have been spent from the burn address\n" + " \"Total_NotaryPay\" (number) Total amount paid to notaries\n" + " \"Estimated_Notarizations_Left\" (number) the estimated amount of notarizations left before the balance is consumed\n" + " \"Estimated_Days_Left\" (number) the estimated amount of days the current balance will last\n" + " \"Estimated_Height\" (number) the estimated block height funds will run out\n" + "}\n" + ); + + if ( ASSETCHAINS_NOTARY_PAY[0] == 0 ) + throw runtime_error("only works for ac_notarypay chains"); + + UniValue result(UniValue::VOBJ); CAmount balance = 0, received = 0; int64_t TotalNotaryPay = 0, NotaryPay, notaleft = 0, daysleft = 0, notarycount; int32_t height, endheight = 0; uint8_t notarypubkeys[64][33] = {0}; + + // pubkey 020000000000000000000000000000000 + balance = checkburnaddress(received, TotalNotaryPay, height, "REDVp3ox1pbcWYCzySadfHhk8UU3HM4k5x"); + + notarycount = komodo_notaries(notarypubkeys, height, chainActive[height]->GetBlockTime()); + NotaryPay = komodo_notarypayamount(height, notarycount)*notarycount; + bool spent = (received != balance); + if ( !spent ) + { + notaleft = ((int64_t)balance - TotalNotaryPay) / NotaryPay; + daysleft = (((ASSETCHAINS_BLOCKTIME * 5) * notaleft) / 3600) / 24; + endheight = (notaleft * 5) + height; + } + + result.push_back(Pair("height", height)); + result.push_back(Pair("balance", ValueFromAmount(balance))); + result.push_back(Pair("spent", spent)); + result.push_back(Pair("Total_NotaryPay", ValueFromAmount(TotalNotaryPay))); + result.push_back(Pair("Estimated_Notarizations_Left", notaleft)); + result.push_back(Pair("Estimated_Days_Left", daysleft)); + result.push_back(Pair("Estimated_Height", endheight)); + return result; +} + UniValue getaddressbalance(const UniValue& params, bool fHelp) { if (fHelp ||params.size() > 2 || params.size() == 0) @@ -1281,8 +1384,13 @@ UniValue getsnapshot(const UniValue& params, bool fHelp) if (params.size() > 0 && !params[0].isNull()) { top = atoi(params[0].get_str().c_str()); - if (top <= 0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, top must be a positive integer"); + if ( top < 0 ) + { + if ( KOMODO_SNAPSHOT_INTERVAL == 0 ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, top must be a positive integer"); + else + top = -1; + } } if ( fHelp || params.size() > 1) @@ -1329,7 +1437,7 @@ UniValue getsnapshot(const UniValue& params, bool fHelp) UniValue getaddresstxids(const UniValue& params, bool fHelp) { - if (fHelp || params.size() > 2) + if (fHelp || params.size() > 2 || params.size() < 1) throw runtime_error( "getaddresstxids (ccvout)\n" "\nReturns the txids for an address(es) (requires addressindex to be enabled).\n" diff --git a/src/rpc/net.cpp b/src/rpc/net.cpp index e280b1a0b..56f221a37 100644 --- a/src/rpc/net.cpp +++ b/src/rpc/net.cpp @@ -219,7 +219,6 @@ int32_t komodo_longestchain() depth--; if ( num > (n >> 1) ) { - extern char ASSETCHAINS_SYMBOL[]; if ( 0 && height != KOMODO_LONGESTCHAIN ) fprintf(stderr,"set %s KOMODO_LONGESTCHAIN <- %d\n",ASSETCHAINS_SYMBOL,height); KOMODO_LONGESTCHAIN = height; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index 1c3cb96f2..578c2f9ad 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -48,6 +48,8 @@ #include +int32_t komodo_notarized_height(int32_t *prevMoMheightp,uint256 *hashp,uint256 *txidp); + using namespace std; extern char ASSETCHAINS_SYMBOL[]; @@ -185,10 +187,13 @@ int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout) void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, int nHeight = 0, int nConfirmations = 0, int nBlockTime = 0) { + uint256 notarized_hash,notarized_desttxid; int32_t prevMoMheight,notarized_height; + notarized_height = komodo_notarized_height(&prevMoMheight,¬arized_hash,¬arized_desttxid); uint256 txid = tx.GetHash(); entry.push_back(Pair("txid", txid.GetHex())); entry.push_back(Pair("overwintered", tx.fOverwintered)); entry.push_back(Pair("version", tx.nVersion)); + entry.push_back(Pair("last_notarized_height", notarized_height)); if (tx.fOverwintered) { entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId))); } diff --git a/src/rpc/register.h b/src/rpc/register.h index 072517d88..245f76e22 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -35,6 +35,10 @@ void RegisterMiningRPCCommands(CRPCTable &tableRPC); /** Register raw transaction RPC commands */ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); +/** Register test transaction RPC commands */ +void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC); + + static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) { RegisterBlockchainRPCCommands(tableRPC); @@ -42,6 +46,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) RegisterMiscRPCCommands(tableRPC); RegisterMiningRPCCommands(tableRPC); RegisterRawTransactionRPCCommands(tableRPC); +#ifdef TESTMODE + RegisterTesttransactionsRPCCommands(tableRPC); +#endif } #endif diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index 3ed082455..5d7fd6d74 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -1,5 +1,6 @@ // Copyright (c) 2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers +// Copyright (c) 2019 The Hush developers // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -45,6 +46,7 @@ using namespace RPCServer; using namespace std; +extern uint16_t ASSETCHAINS_P2PPORT,ASSETCHAINS_RPCPORT; static bool fRPCRunning = false; static bool fRPCInWarmup = true; @@ -454,6 +456,7 @@ static const CRPCCommand vRPCCommands[] = { "oracles", "oracleslist", &oracleslist, true }, { "oracles", "oraclesinfo", &oraclesinfo, true }, { "oracles", "oraclescreate", &oraclescreate, true }, + { "oracles", "oraclesfund", &oraclesfund, true }, { "oracles", "oraclesregister", &oraclesregister, true }, { "oracles", "oraclessubscribe", &oraclessubscribe, true }, { "oracles", "oraclesdata", &oraclesdata, true }, @@ -463,7 +466,15 @@ static const CRPCCommand vRPCCommands[] = { "prices", "prices", &prices, true }, { "prices", "pricesaddress", &pricesaddress, true }, { "prices", "priceslist", &priceslist, true }, + { "prices", "mypriceslist", &mypriceslist, true }, { "prices", "pricesinfo", &pricesinfo, true }, + { "prices", "pricesbet", &pricesbet, true }, + { "prices", "pricessetcostbasis", &pricessetcostbasis, true }, + { "prices", "pricescashout", &pricescashout, true }, + { "prices", "pricesrekt", &pricesrekt, true }, + { "prices", "pricesaddfunding", &pricesaddfunding, true }, + { "prices", "pricesgetorderbook", &pricesgetorderbook, true }, + { "prices", "pricesrefillfund", &pricesrefillfund, true }, // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, @@ -480,12 +491,15 @@ static const CRPCCommand vRPCCommands[] = { "marmara", "marmaralock", &marmara_lock, true }, // Payments - { "payments", "paymentsaddress", &paymentsaddress, true }, - { "payments", "paymentstxidopret", &payments_txidopret, true }, - { "payments", "paymentscreate", &payments_create, true }, - { "payments", "paymentslist", &payments_list, true }, - { "payments", "paymentsinfo", &payments_info, true }, - { "payments", "paymentsfund", &payments_fund, true }, + { "payments", "paymentsaddress", &paymentsaddress, true }, + { "payments", "paymentstxidopret", &payments_txidopret, true }, + { "payments", "paymentscreate", &payments_create, true }, + { "payments", "paymentsairdrop", &payments_airdrop, true }, + { "payments", "paymentsairdroptokens", &payments_airdroptokens, true }, + { "payments", "paymentslist", &payments_list, true }, + { "payments", "paymentsinfo", &payments_info, true }, + { "payments", "paymentsfund", &payments_fund, true }, + { "payments", "paymentsmerge", &payments_merge, true }, { "payments", "paymentsrelease", &payments_release, true }, { "CClib", "cclibaddress", &cclibaddress, true }, @@ -542,6 +556,8 @@ static const CRPCCommand vRPCCommands[] = /* Address index */ { "addressindex", "getaddressmempool", &getaddressmempool, true }, { "addressindex", "getaddressutxos", &getaddressutxos, false }, + { "addressindex", "checknotarization", &checknotarization, false }, + { "addressindex", "getnotarypayinfo", &getnotarypayinfo, false }, { "addressindex", "getaddressdeltas", &getaddressdeltas, false }, { "addressindex", "getaddresstxids", &getaddresstxids, false }, { "addressindex", "getaddressbalance", &getaddressbalance, false }, @@ -565,10 +581,6 @@ static const CRPCCommand vRPCCommands[] = { "util", "reconsiderblock", &reconsiderblock, true }, /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, true }, - { "hidden", "test_ac", &test_ac, true }, - { "hidden", "test_heirmarker", &test_heirmarker, true }, - { "hidden", "test_proof", &test_proof, true }, - { "hidden", "test_burntx", &test_burntx, true }, #ifdef ENABLE_WALLET @@ -639,6 +651,7 @@ static const CRPCCommand vRPCCommands[] = { "wallet", "z_importviewingkey", &z_importviewingkey, true }, { "wallet", "z_exportwallet", &z_exportwallet, true }, { "wallet", "z_importwallet", &z_importwallet, true }, + { "wallet", "opreturn_burn", &opreturn_burn, true }, // TODO: rearrange into another category { "disclosure", "z_getpaymentdisclosure", &z_getpaymentdisclosure, true }, @@ -843,21 +856,30 @@ UniValue CRPCTable::execute(const std::string &strMethod, const UniValue ¶ms std::string HelpExampleCli(const std::string& methodname, const std::string& args) { - return "> komodo-cli " + methodname + " " + args + "\n"; + if ( ASSETCHAINS_SYMBOL[0] == 0 ) { + return "> komodo-cli " + methodname + " " + args + "\n"; + } else if ((strncmp(ASSETCHAINS_SYMBOL, "HUSH3", 5) == 0) ) { + return "> hush-cli " + methodname + " " + args + "\n"; + } else { + return "> komodo-cli -ac_name=" + strprintf("%s", ASSETCHAINS_SYMBOL) + " " + methodname + " " + args + "\n"; + } } std::string HelpExampleRpc(const std::string& methodname, const std::string& args) { - return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " - "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:7771/\n"; + return "> curl --user myusername --data-binary '{\"jsonrpc\": \"1.0\", \"id\":\"curltest\", " + "\"method\": \"" + methodname + "\", \"params\": [" + args + "] }' -H 'content-type: text/plain;' http://127.0.0.1:" + to_string(ASSETCHAINS_RPCPORT) + "/\n"; } string experimentalDisabledHelpMsg(const string& rpc, const string& enableArg) { + string daemon = ASSETCHAINS_SYMBOL[0] == 0 ? "komodod" : "hushd"; + string ticker = ASSETCHAINS_SYMBOL[0] == 0 ? "komodo" : ASSETCHAINS_SYMBOL; + return "\nWARNING: " + rpc + " is disabled.\n" - "To enable it, restart zcashd with the -experimentalfeatures and\n" + "To enable it, restart " + daemon + " with the -experimentalfeatures and\n" "-" + enableArg + " commandline options, or add these two lines\n" - "to the zcash.conf file:\n\n" + "to the " + ticker + ".conf file:\n\n" "experimentalfeatures=1\n" + enableArg + "=1\n"; } diff --git a/src/rpc/server.h b/src/rpc/server.h index 8e0054e6f..27e7288cd 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -210,6 +210,8 @@ extern UniValue getaddresstxids(const UniValue& params, bool fHelp); extern UniValue getsnapshot(const UniValue& params, bool fHelp); extern UniValue getaddressbalance(const UniValue& params, bool fHelp); extern UniValue getpeerinfo(const UniValue& params, bool fHelp); +extern UniValue checknotarization(const UniValue& params, bool fHelp); +extern UniValue getnotarypayinfo(const UniValue& params, bool fHelp); extern UniValue ping(const UniValue& params, bool fHelp); extern UniValue addnode(const UniValue& params, bool fHelp); extern UniValue disconnectnode(const UniValue& params, bool fHelp); @@ -265,12 +267,14 @@ extern UniValue oraclesaddress(const UniValue& params, bool fHelp); extern UniValue oracleslist(const UniValue& params, bool fHelp); extern UniValue oraclesinfo(const UniValue& params, bool fHelp); extern UniValue oraclescreate(const UniValue& params, bool fHelp); +extern UniValue oraclesfund(const UniValue& params, bool fHelp); extern UniValue oraclesregister(const UniValue& params, bool fHelp); extern UniValue oraclessubscribe(const UniValue& params, bool fHelp); extern UniValue oraclesdata(const UniValue& params, bool fHelp); extern UniValue oraclessamples(const UniValue& params, bool fHelp); extern UniValue pricesaddress(const UniValue& params, bool fHelp); extern UniValue priceslist(const UniValue& params, bool fHelp); +extern UniValue mypriceslist(const UniValue& params, bool fHelp); extern UniValue pricesinfo(const UniValue& params, bool fHelp); extern UniValue pegsaddress(const UniValue& params, bool fHelp); extern UniValue marmaraaddress(const UniValue& params, bool fHelp); @@ -285,8 +289,11 @@ extern UniValue marmara_lock(const UniValue& params, bool fHelp); extern UniValue paymentsaddress(const UniValue& params, bool fHelp); extern UniValue payments_release(const UniValue& params, bool fHelp); extern UniValue payments_fund(const UniValue& params, bool fHelp); +extern UniValue payments_merge(const UniValue& params, bool fHelp); extern UniValue payments_txidopret(const UniValue& params, bool fHelp); extern UniValue payments_create(const UniValue& params, bool fHelp); +extern UniValue payments_airdrop(const UniValue& params, bool fHelp); +extern UniValue payments_airdroptokens(const UniValue& params, bool fHelp); extern UniValue payments_info(const UniValue& params, bool fHelp); extern UniValue payments_list(const UniValue& params, bool fHelp); @@ -466,6 +473,7 @@ extern UniValue z_shieldcoinbase(const UniValue& params, bool fHelp); // in rpcw extern UniValue z_getoperationstatus(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_getoperationresult(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_listoperationids(const UniValue& params, bool fHelp); // in rpcwallet.cpp +extern UniValue opreturn_burn(const UniValue& params, bool fHelp); // in rpcwallet.cpp extern UniValue z_validateaddress(const UniValue& params, bool fHelp); // in rpcmisc.cpp extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp); // in rpcdisclosure.cpp extern UniValue z_validatepaymentdisclosure(const UniValue ¶ms, bool fHelp); // in rpcdisclosure.cpp @@ -497,11 +505,14 @@ extern UniValue paxdeposit(const UniValue& params, bool fHelp); extern UniValue paxwithdraw(const UniValue& params, bool fHelp); extern UniValue prices(const UniValue& params, bool fHelp); +extern UniValue pricesbet(const UniValue& params, bool fHelp); +extern UniValue pricessetcostbasis(const UniValue& params, bool fHelp); +extern UniValue pricescashout(const UniValue& params, bool fHelp); +extern UniValue pricesrekt(const UniValue& params, bool fHelp); +extern UniValue pricesaddfunding(const UniValue& params, bool fHelp); +extern UniValue pricesgetorderbook(const UniValue& params, bool fHelp); +extern UniValue pricesrefillfund(const UniValue& params, bool fHelp); + -// test rpc: -extern UniValue test_ac(const UniValue& params, bool fHelp); -extern UniValue test_heirmarker(const UniValue& params, bool fHelp); -extern UniValue test_burntx(const UniValue& params, bool fHelp); -extern UniValue test_proof(const UniValue& params, bool fHelp); #endif // BITCOIN_RPCSERVER_H diff --git a/src/rpc/testtransactions.cpp b/src/rpc/testtransactions.cpp new file mode 100644 index 000000000..ddf7c8895 --- /dev/null +++ b/src/rpc/testtransactions.cpp @@ -0,0 +1,268 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include + +#include "amount.h" +#include "chain.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "crosschain.h" +#include "base58.h" +#include "consensus/validation.h" +#include "cc/eval.h" +#include "main.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "streams.h" +#include "sync.h" +#include "util.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" + +#include + +#include + +#include + + +#include "cc/CCinclude.h" +#include "cc/CCPrices.h" + +using namespace std; + +int32_t ensure_CCrequirements(uint8_t evalcode); + +UniValue test_ac(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 4)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_HEIR) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + std::vector pubkey1; + std::vector pubkey2; + + pubkey1 = ParseHex(params[0].get_str().c_str()); + pubkey2 = ParseHex(params[1].get_str().c_str()); + + CPubKey pk1 = pubkey2pk(pubkey1); + CPubKey pk2 = pubkey2pk(pubkey2); + + if (!pk1.IsValid() || !pk2.IsValid()) + throw runtime_error("invalid pubkey\n"); + + int64_t txfee = 10000; + int64_t amount = atoll(params[2].get_str().c_str()) * COIN; + uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); + + if (normalInputs < txfee + amount) + throw runtime_error("not enough normals\n"); + + mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); +} + +UniValue test_heirmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_HEIR) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(fundingtxid, 1)); + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); +} + +UniValue test_burntx(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + + mtx.vin.push_back(CTxIn(tokenid, 0)); + mtx.vin.push_back(CTxIn(tokenid, 1)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); + + std::vector voutPubkeys; + voutPubkeys.push_back(burnpk); + + cp = CCinit(&C, EVAL_TOKENS); + + std::vector vopret; + GetNonfungibleData(tokenid, vopret); + if (vopret.size() > 0) + cp->additionalTokensEvalcode2 = vopret.begin()[0]; + + uint8_t tokenpriv[33]; + char unspendableTokenAddr[64]; + CPubKey unspPk = GetUnspendable(cp, tokenpriv); + GetCCaddress(cp, unspendableTokenAddr, unspPk); + CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); +} + +UniValue test_proof(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + std::vectorproof; + + if (fHelp || (params.size() != 2)) + throw runtime_error("incorrect params\n"); + + + proof = ParseHex(params[0].get_str()); + uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); + + std::vector txids; + + CMerkleBlock merkleBlock; + if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { + result.push_back(Pair("error", "could not unmarshal proof")); + return result; + } + uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); + + result.push_back(Pair("source_root", merkleRoot.GetHex())); + + for (int i = 0; i < txids.size(); i++) + std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; + + + std::vector vMatches(txids.size()); + for (auto v : vMatches) v = true; + CPartialMerkleTree verifTree(txids, vMatches); + + result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); + + if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { + fprintf(stderr, "invalid proof for this cointxid\n"); + } + + std::vector vMerkleTree; + bool f; + ::BuildMerkleTree(&f, txids, vMerkleTree); + + std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); + + uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); + result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); + + return result; +} + +extern CScript prices_costbasisopret(uint256 bettxid, CPubKey mypk, int32_t height, int64_t costbasis); +UniValue test_pricesmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_PRICES) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 bettxid = Parseuint256((char *)params[0].get_str().c_str()); + + cp = CCinit(&C, EVAL_PRICES); + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(bettxid, 1)); + mtx.vout.push_back(CTxOut(1000, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)); + + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, prices_costbasisopret(bettxid, myPubkey, 100, 100))); +} + + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + + /* Not shown in help */ + { "hidden", "test_ac", &test_ac, true }, + { "hidden", "test_heirmarker", &test_heirmarker, true }, + { "hidden", "test_proof", &test_proof, true }, + { "hidden", "test_burntx", &test_burntx, true }, + { "hidden", "test_pricesmarker", &test_pricesmarker, true } +}; + +void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +} \ No newline at end of file diff --git a/src/script/script_ext.cpp b/src/script/script_ext.cpp index 532e39d6c..f1d903946 100644 --- a/src/script/script_ext.cpp +++ b/src/script/script_ext.cpp @@ -85,6 +85,7 @@ const CScriptExt &CScriptExt::AddCheckLockTimeVerify(int64_t unlocktime) const *((CScript *)this) << OP_DROP; return *this; } + return *this; } // combined CLTV script and P2PKH diff --git a/src/txdb.cpp b/src/txdb.cpp index 4c9ea31ca..82885044e 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -458,13 +458,13 @@ uint32_t komodo_segid32(char *coinaddr); {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} \ }; -int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector > &vaddr, UniValue *ret) +bool CBlockTreeDB::Snapshot2(std::map &addressAmounts, UniValue *ret) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; int64_t utxos = 0; int64_t ignoredAddresses = 0, cryptoConditionsUTXOs = 0, cryptoConditionsTotals = 0; DECLARE_IGNORELIST boost::scoped_ptr iter(NewIterator()); - std::map addressAmounts; + //std::map addressAmounts; for (iter->SeekToLast(); iter->Valid(); iter->Prev()) { boost::this_thread::interruption_point(); @@ -481,45 +481,46 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector try { CAmount nValue; iter->GetValue(nValue); + if ( nValue == 0 ) + continue; getAddressFromIndex(indexKey.type, indexKey.hashBytes, address); if ( indexKey.type == 3 ) { cryptoConditionsUTXOs++; cryptoConditionsTotals += nValue; + total += nValue; continue; } - if ( nValue > dustthreshold ) + std::map ::iterator ignored = ignoredMap.find(address); + if (ignored != ignoredMap.end()) { - std::map ::iterator ignored = ignoredMap.find(address); - if (ignored != ignoredMap.end()) - { - fprintf(stderr,"ignoring %s\n", address.c_str()); - ignoredAddresses++; - continue; - } - std::map ::iterator pos = addressAmounts.find(address); - if ( pos == addressAmounts.end() ) - { - // insert new address + utxo amount - //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); - addressAmounts[address] = nValue; - totalAddresses++; - } - else - { - // update unspent tally for this address - //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); - addressAmounts[address] += nValue; - } - //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); - // total += nValue; - utxos++; - } //else fprintf(stderr,"ignoring amount=0 UTXO for %s\n", address.c_str()); + fprintf(stderr,"ignoring %s\n", address.c_str()); + ignoredAddresses++; + continue; + } + std::map ::iterator pos = addressAmounts.find(address); + if ( pos == addressAmounts.end() ) + { + // insert new address + utxo amount + //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); + addressAmounts[address] = nValue; + totalAddresses++; + } + else + { + // update unspent tally for this address + //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); + addressAmounts[address] += nValue; + } + //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); + // total += nValue; + utxos++; + total += nValue; } catch (const std::exception& e) { fprintf(stderr, "DONE %s: LevelDB addressindex exception! - %s\n", __func__, e.what()); - break; + return false; //break; this means failiure of DB? we need to exit here if so for consensus code! } } } @@ -530,30 +531,18 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector } } //fprintf(stderr, "total=%f, totalAddresses=%li, utxos=%li, ignored=%li\n", (double) total / COIN, totalAddresses, utxos, ignoredAddresses); - for (std::pair element : addressAmounts) - vaddr.push_back( make_pair(element.second, element.first) ); - std::sort(vaddr.rbegin(), vaddr.rend()); - int topN = 0; - for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) - { - total += it->first; - topN++; - // If requested, only show top N addresses in output JSON - if ( top == topN ) - break; - } + // this is for the snapshot RPC, you can skip this by passing a 0 as the last argument. if (ret) { - // Total amount in this snapshot, which is less than circulating supply if top parameter is used - // Use the address_total for a total of all address included when using top parameter. - ret->push_back(make_pair("total", (double) (total+cryptoConditionsTotals)/ COIN )); + // Total circulating supply without CC vouts. + ret->push_back(make_pair("total", (double) (total)/ COIN )); // Average amount in each address of this snapshot ret->push_back(make_pair("average",(double) (total/COIN) / totalAddresses )); // Total number of utxos processed in this snaphot ret->push_back(make_pair("utxos", utxos)); // Total number of addresses in this snaphot - ret->push_back(make_pair("total_addresses", top ? top : totalAddresses )); + ret->push_back(make_pair("total_addresses", totalAddresses )); // Total number of ignored addresses in this snaphot ret->push_back(make_pair("ignored_addresses", ignoredAddresses)); // Total number of crypto condition utxos we skipped @@ -561,22 +550,39 @@ int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold, int32_t top ,std::vector // Total value of skipped crypto condition utxos ret->push_back(make_pair("cc_utxo_value", (double) cryptoConditionsTotals / COIN)); // total of all the address's, does not count coins in CC vouts. - ret->push_back(make_pair("address_total", (double) total/ COIN )); + ret->push_back(make_pair("total_includeCCvouts", (double) (total+cryptoConditionsTotals)/ COIN )); // The snapshot finished at this block height ret->push_back(make_pair("ending_height", chainActive.Height())); } - return(topN); + return true; } +extern std::vector > vAddressSnapshot; + UniValue CBlockTreeDB::Snapshot(int top) { int topN = 0; std::vector > vaddr; + //std::vector >> tokenids; + std::map addressAmounts; UniValue result(UniValue::VOBJ); UniValue addressesSorted(UniValue::VARR); result.push_back(Pair("start_time", (int) time(NULL))); - if ( Snapshot2(0,top,vaddr,&result) != 0 ) + if ( (vAddressSnapshot.size() > 0 && top < 0) || (Snapshot2(addressAmounts,&result) && top >= 0) ) { + if ( top > -1 ) + { + for (std::pair element : addressAmounts) + vaddr.push_back( make_pair(element.second, element.first) ); + std::sort(vaddr.rbegin(), vaddr.rend()); + } + else + { + for ( auto address : vAddressSnapshot ) + vaddr.push_back(make_pair(address.first, CBitcoinAddress(address.second).ToString())); + top = vAddressSnapshot.size(); + } + int topN = 0; for (std::vector>::iterator it = vaddr.begin(); it!=vaddr.end(); ++it) { UniValue obj(UniValue::VOBJ); @@ -712,6 +718,8 @@ bool CBlockTreeDB::LoadBlockIndexGuts() pindexNew->nTx = diskindex.nTx; pindexNew->nSproutValue = diskindex.nSproutValue; pindexNew->nSaplingValue = diskindex.nSaplingValue; + pindexNew->segid = diskindex.segid; + pindexNew->nNotaryPay = diskindex.nNotaryPay; //fprintf(stderr,"loadguts ht.%d\n",pindexNew->GetHeight()); // Consistency checks auto header = pindexNew->GetBlockHeader(); diff --git a/src/txdb.h b/src/txdb.h index b4c4cd6bd..195f4c183 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -116,7 +116,7 @@ public: bool LoadBlockIndexGuts(); bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); - int32_t Snapshot2(int64_t dustthreshold, int32_t top,std::vector > &vaddr, UniValue *ret); + bool Snapshot2(std::map &addressAmounts, UniValue *ret); }; #endif // BITCOIN_TXDB_H diff --git a/src/util.cpp b/src/util.cpp index 980d82ac9..1aa079085 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -394,27 +394,23 @@ void ParseParameters(int argc, const char* const argv[]) } } +// split string using by space or comma as a delimiter char void SplitStr(const std::string& strVal, std::vector &outVals) { stringstream ss(strVal); - std::string str; - while ( ss.peek() == ' ' ) - ss.ignore(); - - while ( ss >> str ) - { - if ( str.size() == 0 ) - continue; - if ( str[str.size()-1] == ',' ) - str.resize(str.size()-1); - outVals.push_back(str); - while ( ss.peek() == ' ' ) - ss.ignore(); - if ( ss.peek() == ',' ) - ss.ignore(); - while ( ss.peek() == ' ' ) + while (!ss.eof()) { + int c; + std::string str; + + while (std::isspace(ss.peek())) ss.ignore(); + + while ((c = ss.get()) != EOF && !std::isspace(c) && c != ',') + str += c; + + if (!str.empty()) + outVals.push_back(str); } } diff --git a/src/util.h b/src/util.h index dc421092a..617faaf36 100644 --- a/src/util.h +++ b/src/util.h @@ -287,6 +287,9 @@ template void TraceThread(const char* name, Callable func) } } +// split string using by space or comma as a delimiter char +void SplitStr(const std::string& strVal, std::vector &outVals); + #define KOMODO_ASSETCHAIN_MAXLEN 65 diff --git a/src/wallet/db.h b/src/wallet/db.h index 19b9b6079..3aaf3d34b 100644 --- a/src/wallet/db.h +++ b/src/wallet/db.h @@ -33,7 +33,11 @@ #include -#include +#ifdef BUILD_ROGUE + #include "../depends/x86_64-unknown-linux-gnu/include/db_cxx.h" +#else + #include +#endif extern unsigned int nWalletDBUpdated; diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 6fe061727..aa5876e23 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -59,6 +59,7 @@ #include #include "komodo_defs.h" +#include using namespace std; @@ -69,9 +70,10 @@ extern std::string ASSETCHAINS_OVERRIDE_PUBKEY; const std::string ADDR_TYPE_SPROUT = "sprout"; const std::string ADDR_TYPE_SAPLING = "sapling"; extern UniValue TxJoinSplitToJSON(const CTransaction& tx); +extern int32_t KOMODO_INSYNC; uint32_t komodo_segid32(char *coinaddr); int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); -int32_t komodo_isnotaryvout(char *coinaddr); // from ac_private chains only +int32_t komodo_isnotaryvout(char *coinaddr,uint32_t tiptime); // from ac_private chains only CBlockIndex *komodo_getblockindex(uint256 hash); int64_t nWalletUnlockTime; @@ -83,6 +85,7 @@ UniValue z_getoperationstatus_IMPL(const UniValue&, bool); #define PLAN_NAME_MAX 8 #define VALID_PLAN_NAME(x) (strlen(x) <= PLAN_NAME_MAX) +#define THROW_IF_SYNCING(INSYNC) if (INSYNC == 0) { throw runtime_error(strprintf("%s: Chain still syncing at height %d, aborting to prevent linkability analysis!",__FUNCTION__,chainActive.Tip()->GetHeight())); } int tx_height( const uint256 &hash ); @@ -460,7 +463,6 @@ static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtr throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); } -int32_t komodo_isnotaryvout(char *coinaddr); UniValue sendtoaddress(const UniValue& params, bool fHelp) { @@ -493,7 +495,7 @@ UniValue sendtoaddress(const UniValue& params, bool fHelp) if ( ASSETCHAINS_PRIVATE != 0 && AmountFromValue(params[1]) > 0 ) { - if ( komodo_isnotaryvout((char *)params[0].get_str().c_str()) == 0 ) + if ( komodo_isnotaryvout((char *)params[0].get_str().c_str(),chainActive.LastTip()->nTime) == 0 ) { throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); } @@ -4249,6 +4251,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + THROW_IF_SYNCING(KOMODO_INSYNC); + // Check that the from address is valid. auto fromaddress = params[0].get_str(); bool fromTaddr = false; @@ -4349,8 +4353,9 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) //else if ( ASSETCHAINS_PRIVATE != 0 && komodo_isnotaryvout((char *)address.c_str()) == 0 ) // throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); - if (setAddress.count(address)) - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+address); + // Allowing duplicate receivers helps various HushList protocol operations + //if (setAddress.count(address)) + // throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+address); setAddress.insert(address); UniValue memoValue = find_value(o, "memo"); @@ -4558,6 +4563,8 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + THROW_IF_SYNCING(KOMODO_INSYNC); + // Validate the from address auto fromaddress = params[0].get_str(); bool isFromWildcard = fromaddress == "*"; @@ -4818,6 +4825,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); + THROW_IF_SYNCING(KOMODO_INSYNC); + bool useAnyUTXO = false; bool useAnySprout = false; bool useAnySapling = false; @@ -5581,7 +5590,7 @@ UniValue payments_release(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; if ( fHelp || params.size() != 1 ) - throw runtime_error("paymentsrelease \"[%22createtxid%22,amount]\"\n"); + throw runtime_error("paymentsrelease \"[%22createtxid%22,amount,(skipminimum)]\"\n"); if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; @@ -5603,6 +5612,19 @@ UniValue payments_fund(const UniValue& params, bool fHelp) return(PaymentsFund(cp,(char *)params[0].get_str().c_str())); } +UniValue payments_merge(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsmerge \"[%22createtxid%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsMerge(cp,(char *)params[0].get_str().c_str())); +} + UniValue payments_txidopret(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; @@ -5629,6 +5651,32 @@ UniValue payments_create(const UniValue& params, bool fHelp) return(PaymentsCreate(cp,(char *)params[0].get_str().c_str())); } +UniValue payments_airdrop(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("paymentsairdrop \"[lockedblocks,minamount,mintoaddress,top,bottom,fixedFlag,%22excludeAddress%22,...,%22excludeAddressN%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsAirdrop(cp,(char *)params[0].get_str().c_str())); +} + +UniValue payments_airdroptokens(const UniValue& params, bool fHelp) +{ + struct CCcontract_info *cp,C; + if ( fHelp || params.size() != 1 ) + throw runtime_error("payments_airdroptokens \"[%22tokenid%22,lockedblocks,minamount,mintoaddress,top,bottom,fixedFlag,%22excludePubKey%22,...,%22excludePubKeyN%22]\"\n"); + if ( ensure_CCrequirements(EVAL_PAYMENTS) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + cp = CCinit(&C,EVAL_PAYMENTS); + return(PaymentsAirdropTokens(cp,(char *)params[0].get_str().c_str())); +} + UniValue payments_info(const UniValue& params, bool fHelp) { struct CCcontract_info *cp,C; @@ -6713,6 +6761,26 @@ UniValue oraclesinfo(const UniValue& params, bool fHelp) return(OracleInfo(txid)); } +UniValue oraclesfund(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); uint256 txid; std::string hex; + if ( fHelp || params.size() != 1 ) + throw runtime_error("oraclesfund oracletxid\n"); + if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + const CKeyStore& keystore = *pwalletMain; + LOCK2(cs_main, pwalletMain->cs_wallet); + txid = Parseuint256((char *)params[0].get_str().c_str()); + hex = OracleFund(0,txid); + RETURN_IF_ERROR(CCerror); + if ( hex.size() > 0 ) + { + result.push_back(Pair("result", "success")); + result.push_back(Pair("hex", hex)); + } else ERR_RESULT("couldnt fund with oracle txid"); + return(result); +} + UniValue oraclesregister(const UniValue& params, bool fHelp) { UniValue result(UniValue::VOBJ); uint256 txid; int64_t datafee; std::string hex; @@ -6759,17 +6827,17 @@ UniValue oraclessubscribe(const UniValue& params, bool fHelp) UniValue oraclessamples(const UniValue& params, bool fHelp) { - UniValue result(UniValue::VOBJ); uint256 txid,batontxid; int32_t num; + UniValue result(UniValue::VOBJ); uint256 txid; int32_t num; char *batonaddr; if ( fHelp || params.size() != 3 ) - throw runtime_error("oraclessamples oracletxid batonutxo num\n"); + throw runtime_error("oraclessamples oracletxid batonaddress num\n"); if ( ensure_CCrequirements(EVAL_ORACLES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); const CKeyStore& keystore = *pwalletMain; LOCK2(cs_main, pwalletMain->cs_wallet); txid = Parseuint256((char *)params[0].get_str().c_str()); - batontxid = Parseuint256((char *)params[1].get_str().c_str()); + batonaddr = (char *)params[1].get_str().c_str(); num = atoi((char *)params[2].get_str().c_str()); - return(OracleDataSamples(txid,batontxid,num)); + return(OracleDataSamples(txid,batonaddr,num)); } UniValue oraclesdata(const UniValue& params, bool fHelp) @@ -6919,25 +6987,61 @@ UniValue faucetget(const UniValue& params, bool fHelp) return(result); } +uint32_t pricesGetParam(UniValue param) { + uint32_t filter = 0; + if (STR_TOLOWER(param.get_str()) == "all") + filter = 0; + else if (STR_TOLOWER(param.get_str()) == "open") + filter = 1; + else if (STR_TOLOWER(param.get_str()) == "closed") + filter = 2; + else + throw runtime_error("incorrect parameter\n"); + return filter; +} + UniValue priceslist(const UniValue& params, bool fHelp) { - if ( fHelp || params.size() > 0 ) - throw runtime_error("priceslist\n"); + if ( fHelp || params.size() != 0 && params.size() != 1) + throw runtime_error("priceslist [all|open|closed]\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - return(PricesList()); + uint32_t filter = 0; + if (params.size() == 1) + filter = pricesGetParam(params[0]); + + CPubKey emptypk; + + return(PricesList(filter, emptypk)); +} + +UniValue mypriceslist(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0 && params.size() != 1) + throw runtime_error("mypriceslist [all|open|closed]\n"); + if (ensure_CCrequirements(EVAL_PRICES) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint32_t filter = 0; + if (params.size() == 1) + filter = pricesGetParam(params[0]); + CPubKey mypk = pubkey2pk(Mypubkey()); + + return(PricesList(filter, mypk)); } UniValue pricesinfo(const UniValue& params, bool fHelp) { uint256 bettxid; int32_t height; - if ( fHelp || params.size() != 2 ) - throw runtime_error("pricesinfo fundingtxid\n"); + if ( fHelp || params.size() != 1 && params.size() != 2) + throw runtime_error("pricesinfo bettxid [height]\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); bettxid = Parseuint256((char *)params[0].get_str().c_str()); - height = atoi(params[1].get_str().c_str()); - return(PricesInfo(bettxid,height)); + height = 0; + if (params.size() == 2) + height = atoi(params[1].get_str().c_str()); + return(PricesInfo(bettxid, height)); } UniValue dicefund(const UniValue& params, bool fHelp) @@ -7276,9 +7380,13 @@ UniValue tokencreate(const UniValue& params, bool fHelp) if (params.size() == 4) { nonfungibleData = ParseHex(params[3].get_str()); - if (nonfungibleData.size() > 10000) // opret limit + if (nonfungibleData.size() > IGUANA_MAXSCRIPTSIZE) // opret limit { - ERR_RESULT("Non-fungible data must be <= 10000"); + ERR_RESULT("Non-fungible data size must be <= " + std::to_string(IGUANA_MAXSCRIPTSIZE)); + return(result); + } + if( nonfungibleData.empty() ) { + ERR_RESULT("Non-fungible data incorrect"); return(result); } } @@ -7935,170 +8043,34 @@ void RegisterWalletRPCCommands(CRPCTable &tableRPC) tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); } -UniValue test_ac(const UniValue& params, bool fHelp) +UniValue opreturn_burn(const UniValue& params, bool fHelp) { - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 4)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - std::vector pubkey1; - std::vector pubkey2; - - pubkey1 = ParseHex(params[0].get_str().c_str()); - pubkey2 = ParseHex(params[1].get_str().c_str()); - - CPubKey pk1 = pubkey2pk(pubkey1); - CPubKey pk2 = pubkey2pk(pubkey2); - - if(!pk1.IsValid() || !pk2.IsValid()) - throw runtime_error("invalid pubkey\n"); - - int64_t txfee = 10000; - int64_t amount = atoll(params[2].get_str().c_str()) * COIN; - uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); + std::vector vHexStr; CScript opret; int32_t txfee = 10000; + if (fHelp || (params.size() != 2)) + throw runtime_error("amount to burn, hexstring to send\n"); + struct CCcontract_info *cp, C; UniValue ret(UniValue::VOBJ); + if (ensure_CCrequirements(EVAL_ORACLES) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + cp = CCinit(&C, EVAL_ORACLES); + + CAmount nAmount = AmountFromValue(params[0]); + if (nAmount <= 10000) + throw JSONRPCError(RPC_TYPE_ERROR, "must burn at least 10000 sat"); + vHexStr = ParseHex(params[1].get_str()); + if ( vHexStr.size() == 0 ) + throw JSONRPCError(RPC_TYPE_ERROR, "hexstring is not valid."); CPubKey myPubkey = pubkey2pk(Mypubkey()); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); - - if( normalInputs < txfee + amount) + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, nAmount, 60); + if (normalInputs < nAmount) throw runtime_error("not enough normals\n"); - - mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); -} - -UniValue test_heirmarker(const UniValue& params, bool fHelp) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - mtx.vin.push_back(CTxIn(fundingtxid, 1)); - mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); -} - -UniValue test_burntx(const UniValue& params, bool fHelp) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); - - mtx.vin.push_back(CTxIn(tokenid, 0)); - mtx.vin.push_back(CTxIn(tokenid, 1)); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); - - std::vector voutPubkeys; - voutPubkeys.push_back(burnpk); - cp = CCinit(&C, EVAL_TOKENS); - - std::vector vopret; - GetNonfungibleData(tokenid, vopret); - if (vopret.size() > 0) - cp->additionalTokensEvalcode2 = vopret.begin()[0]; - - uint8_t tokenpriv[33]; - char unspendableTokenAddr[64]; - CPubKey unspPk = GetUnspendable(cp, tokenpriv); - GetCCaddress(cp, unspendableTokenAddr, unspPk); - CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); -} - -UniValue test_proof(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); - std::vectorproof; - - if (fHelp || (params.size() != 2)) - throw runtime_error("incorrect params\n"); - - - proof = ParseHex(params[0].get_str()); - uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); - - std::vector txids; - - CMerkleBlock merkleBlock; - if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { - result.push_back(Pair("error", "could not unmarshal proof")); - return result; - } - uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); - - result.push_back(Pair("source_root", merkleRoot.GetHex())); - - for (int i = 0; i < txids.size(); i++) - std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; - - - std::vector vMatches(txids.size()); - for (auto v : vMatches) v = true; - CPartialMerkleTree verifTree(txids, vMatches); - - result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); - - if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { - fprintf(stderr, "invalid proof for this cointxid\n"); - } - - std::vector vMerkleTree; - bool f; - ::BuildMerkleTree(&f, txids, vMerkleTree); - - std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); - - uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); - result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); - - return result; + opret << OP_RETURN << E_MARSHAL(ss << vHexStr); + + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(nAmount,opret)); + ret.push_back(Pair("hex",FinalizeCCTx(0, cp, mtx, myPubkey, txfee, CScript()))); + return(ret); } diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index fa39d204a..4d834ccee 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2874,21 +2874,18 @@ void CWallet::ReacceptWalletTransactions() bool invalid = state.IsInvalid(nDoS); // log rejection and deletion - // printf("ERROR reaccepting wallet transaction %s to mempool, reason: %s, DoS: %d\n", wtx.GetHash().ToString().c_str(), state.GetRejectReason().c_str(), nDoS); + //printf("ERROR reaccepting wallet transaction %s to mempool, reason: %s, DoS: %d\n", wtx.GetHash().ToString().c_str(), state.GetRejectReason().c_str(), nDoS); - if (!wtx.IsCoinBase() && invalid && nDoS > 0) + if (!wtx.IsCoinBase() && invalid && nDoS > 0 && state.GetRejectReason() != "tx-overwinter-expired") { LogPrintf("erasing transaction %s\n", wtx.GetHash().GetHex().c_str()); vwtxh.push_back(wtx.GetHash()); } } } - if ( IsInitialBlockDownload() == 0 ) + for (auto hash : vwtxh) { - for (auto hash : vwtxh) - { - EraseFromWallet(hash); - } + EraseFromWallet(hash); } } @@ -2896,7 +2893,7 @@ bool CWalletTx::RelayWalletTransaction() { if ( pwallet == 0 ) { - fprintf(stderr,"unexpected null pwallet in RelayWalletTransaction\n"); + //fprintf(stderr,"unexpected null pwallet in RelayWalletTransaction\n"); return(false); } assert(pwallet->GetBroadcastTransactions()); diff --git a/src/wallet/walletdb.cpp b/src/wallet/walletdb.cpp index efba38d9e..d2d90a85f 100644 --- a/src/wallet/walletdb.cpp +++ b/src/wallet/walletdb.cpp @@ -41,6 +41,7 @@ using namespace std; static uint64_t nAccountingEntryNumber = 0; static list deadTxns; +extern CBlockIndex *komodo_blockindex(uint256 hash); // // CWalletDB @@ -487,7 +488,8 @@ ReadKeyValue(CWallet* pwallet, CDataStream& ssKey, CDataStream& ssValue, auto verifier = libzcash::ProofVerifier::Strict(); // ac_public chains set at height like KMD and ZEX, will force a rescan if we dont ignore this error: bad-txns-acpublic-chain // there cannot be any ztx in the wallet on ac_public chains that started from block 1, so this wont affect those. - if ( !(CheckTransaction(0,wtx, state, verifier) && (wtx.GetHash() == hash) && state.IsValid()) && (state.GetRejectReason() != "bad-txns-acpublic-chain") ) + // PIRATE fails this check for notary nodes, need exception. Triggers full rescan without it. + if ( !(CheckTransaction(0,wtx, state, verifier) && (wtx.GetHash() == hash) && state.IsValid()) && (state.GetRejectReason() != "bad-txns-acpublic-chain" && state.GetRejectReason() != "bad-txns-acprivacy-chain") ) { //fprintf(stderr, "tx failed: %s rejectreason.%s\n", wtx.GetHash().GetHex().c_str(), state.GetRejectReason().c_str()); // vin-empty on staking chains is error relating to a failed staking tx, that for some unknown reason did not fully erase. save them here to erase and re-add later on. @@ -964,21 +966,20 @@ DBErrors CWalletDB::LoadWallet(CWallet* pwallet) // staking chains with vin-empty error is a failed staking tx. // we remove then re add the tx here to stop needing a full rescan, which does not actually fix the problem. int32_t reAdded = 0; - CWalletDB walletdb(pwallet->strWalletFile, "r+", false); BOOST_FOREACH (uint256& hash, deadTxns) { - fprintf(stderr, "Removing corrupt tx from wallet.%s\n", hash.ToString().c_str()); + fprintf(stderr, "Removing possible orphaned staking transaction from wallet.%s\n", hash.ToString().c_str()); if (!EraseTx(hash)) fprintf(stderr, "could not delete tx.%s\n",hash.ToString().c_str()); - uint256 blockhash; CTransaction tx; - if (GetTransaction(hash,tx,blockhash,true)) + uint256 blockhash; CTransaction tx; CBlockIndex* pindex; + if ( GetTransaction(hash,tx,blockhash,false) && (pindex= komodo_blockindex(blockhash)) != 0 && chainActive.Contains(pindex) ) { CWalletTx wtx(pwallet,tx); - pwallet->AddToWallet(wtx, false, &walletdb); + pwallet->AddToWallet(wtx, true, NULL); reAdded++; } } - fprintf(stderr, "Cleared %li corrupted transactions from wallet. Readded %i known transactions.\n",deadTxns.size(),reAdded); + fprintf(stderr, "Cleared %li orphaned staking transactions from wallet. Readded %i real transactions.\n",deadTxns.size(),reAdded); fNoncriticalErrors = false; deadTxns.clear(); } diff --git a/toolchain-info.sh b/toolchain-info.sh index 0bb39c19b..6a0fc7e75 100755 --- a/toolchain-info.sh +++ b/toolchain-info.sh @@ -1,6 +1,6 @@ #!/bin/bash -tools=("gcc-6" "g++-6" "otool" "nm") +tools=("gcc-8" "g++-8" "otool" "nm") echo "Platform: `uname -a`" echo "-------------------------------------" diff --git a/zcutil/build-mac.sh b/zcutil/build-mac.sh index df4efd05f..89847ddac 100755 --- a/zcutil/build-mac.sh +++ b/zcutil/build-mac.sh @@ -1,6 +1,6 @@ #!/bin/bash -export CC=gcc-6 -export CXX=g++-6 +export CC=gcc-8 +export CXX=g++-8 export LIBTOOL=libtool export AR=ar export RANLIB=ranlib @@ -52,7 +52,7 @@ cd $WD ./autogen.sh CPPFLAGS="-I$PREFIX/include -arch x86_64" LDFLAGS="-L$PREFIX/lib -arch x86_64 -Wl,-no_pie" \ -CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc\@6/6.4.0_2/include/c++/6.4.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Werror -g -Wl,-undefined -Wl,dynamic_lookup' \ +CXXFLAGS='-arch x86_64 -I/usr/local/Cellar/gcc\@8/8.3.0/include/c++/8.3.0/ -I$PREFIX/include -fwrapv -fno-strict-aliasing -Wno-builtin-declaration-mismatch -Werror -g -Wl,-undefined -Wl,dynamic_lookup' \ ./configure --prefix="${PREFIX}" --with-gui=no "$HARDENING_ARG" "$LCOV_ARG" make "$@" V=1 NO_GTEST=1 STATIC=1