diff --git a/qa/rpc-tests/cryptoconditions.py b/qa/rpc-tests/cryptoconditions.py new file mode 100644 index 000000000..d5456e801 --- /dev/null +++ b/qa/rpc-tests/cryptoconditions.py @@ -0,0 +1,690 @@ +#!/usr/bin/env python2 +# Copyright (c) 2018 SuperNET developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +from test_framework.test_framework import BitcoinTestFramework +from test_framework.authproxy import JSONRPCException +from test_framework.util import assert_equal, assert_greater_than, \ + initialize_chain_clean, initialize_chain, start_nodes, start_node, connect_nodes_bi, \ + stop_nodes, sync_blocks, sync_mempools, wait_bitcoinds, rpc_port, assert_raises + +import time +from decimal import Decimal +from random import choice +from string import ascii_uppercase + +def assert_success(result): + assert_equal(result['result'], 'success') + +def assert_error(result): + assert_equal(result['result'], 'error') + +def generate_random_string(length): + random_string = ''.join(choice(ascii_uppercase) for i in range(length)) + return random_string + +class CryptoConditionsTest (BitcoinTestFramework): + + def setup_chain(self): + print("Initializing CC test directory "+self.options.tmpdir) + self.num_nodes = 2 + initialize_chain_clean(self.options.tmpdir, self.num_nodes) + + def setup_network(self, split = False): + print("Setting up network...") + self.addr = "RWPg8B91kfK5UtUN7z6s6TeV9cHSGtVY8D" + self.pubkey = "02676d00110c2cd14ae24f95969e8598f7ccfaa675498b82654a5b5bd57fc1d8cf" + self.privkey = "UqMgxk7ySPNQ4r9nKAFPjkXy6r5t898yhuNCjSZJLg3RAM4WW1m9" + self.addr1 = "RXEXoa1nRmKhMbuZovpcYwQMsicwzccZBp" + self.pubkey1 = "024026d4ad4ecfc1f705a9b42ca64af6d2ad947509c085534a30b8861d756c6ff0" + self.privkey1 = "UtdydP56pGTFmawHzHr1wDrc4oUwCNW1ttX8Pc3KrvH3MA8P49Wi" + self.nodes = start_nodes(self.num_nodes, self.options.tmpdir, + extra_args=[[ + # always give -ac_name as first extra_arg and port as third + '-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node0/REGTEST.conf', + '-port=64367', + '-rpcport=64368', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt' + ], + ['-ac_name=REGTEST', + '-conf='+self.options.tmpdir+'/node1/REGTEST.conf', + '-port=64365', + '-rpcport=64366', + '-regtest', + '-addressindex=1', + '-spentindex=1', + '-ac_supply=5555555', + '-ac_reward=10000000000000', + '-pubkey=' + self.pubkey1, + '-ac_cc=2', + '-whitelist=127.0.0.1', + '-debug', + '-addnode=127.0.0.1:64367', + '--daemon', + '-rpcuser=rt', + '-rpcpassword=rt']] + ) + self.is_network_split = split + self.rpc = self.nodes[0] + self.rpc1 = self.nodes[1] + self.sync_all() + print("Done setting up network") + + def send_and_mine(self, xtn, rpc_connection): + txid = rpc_connection.sendrawtransaction(xtn) + assert txid, 'got txid' + # we need the tx above to be confirmed in the next block + rpc_connection.generate(1) + return txid + + def run_faucet_tests(self): + rpc = self.rpc + rpc1 = self.rpc1 + + # basic sanity tests + result = rpc.getwalletinfo() + assert_greater_than(result['txcount'], 100) + assert_greater_than(result['balance'], 0.0) + balance = result['balance'] + + faucet = rpc.faucetaddress() + assert_equal(faucet['result'], 'success') + # verify all keys look like valid AC addrs, could be better + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: + assert_equal(faucet[x][0], 'R') + + result = rpc.faucetaddress(self.pubkey) + assert_success(result) + # test that additional CCaddress key is returned + for x in ['myCCAddress(Faucet)', 'FaucetCCAddress', 'FaucetCCTokensAddress', 'myaddress', 'FaucetNormalAddress']: + assert_equal(result[x][0], 'R') + + # no funds in the faucet yet + result = rpc.faucetget() + assert_error(result) + + result = rpc.faucetinfo() + assert_success(result) + + result = rpc.faucetfund("0") + assert_error(result) + + result = rpc.faucetfund("-1") + assert_error(result) + + # we need at least 1 + txfee to get + result = rpc.faucetfund("2") + assert_success(result) + assert result['hex'], "hex key found" + + # broadcast the xtn + result = rpc.sendrawtransaction(result['hex']) + txid = result[0] + assert txid, "found txid" + + # we need the tx above to be confirmed in the next block + rpc.generate(1) + self.sync_all() + + result = rpc.getwalletinfo() + # minus one block reward + balance2 = result['balance'] - 100000 + # make sure our balance is less now + assert_greater_than(balance, balance2) + + result = rpc.faucetinfo() + assert_success(result) + assert_greater_than( result['funding'], 0 ) + + # claiming faucet on second node + faucetgethex = rpc1.faucetget() + assert_success(faucetgethex) + assert faucetgethex['hex'], "hex key found" + + balance1 = rpc1.getwalletinfo()['balance'] + + # try to broadcast the faucetget transaction + result = self.send_and_mine(faucetgethex['hex'], rpc1) + assert txid, "transaction broadcasted" + + balance2 = rpc1.getwalletinfo()['balance'] + assert_greater_than(balance2, balance1) + + self.sync_all() + + def run_dice_tests(self): + rpc = self.nodes[0] + rpc1 = self.nodes[1] + self.sync_all() + + # have to generate few blocks on second node to be able to place bets + rpc1.generate(10) + result = rpc1.getbalance() + assert_greater_than(result, 100000) + + dice = rpc.diceaddress() + assert_equal(dice['result'], 'success') + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: + assert_equal(dice[x][0], 'R') + + dice = rpc.diceaddress(self.pubkey) + assert_equal(dice['result'], 'success') + for x in ['myCCAddress(Dice)', 'DiceCCAddress', 'DiceCCTokensAddress', 'myaddress', 'DiceNormalAddress']: + assert_equal(dice[x][0], 'R') + + # no dice created yet + result = rpc.dicelist() + assert_equal(result, []) + + # creating dice plan with too long name (>8 chars) + result = rpc.dicefund("THISISTOOLONG", "10000", "10", "10000", "10", "5") + assert_error(result) + + # creating dice plan with < 100 funding + result = rpc.dicefund("LUCKY","10","1","10000","10","5") + assert_error(result) + + # creating dice plan with 0 blocks timeout + result = rpc.dicefund("LUCKY","10","1","10000","10","0") + assert_error(result) + + # creating dice plan + dicefundtx = rpc.dicefund("LUCKY","1000","1","800","10","5") + diceid = self.send_and_mine(dicefundtx['hex'], rpc) + + # checking if it in plans list now + result = rpc.dicelist() + assert_equal(result[0], diceid) + + # set dice name for futher usage + dicename = "LUCKY" + + # adding zero funds to plan + result = rpc.diceaddfunds(dicename,diceid,"0") + assert_error(result) + + # adding negative funds to plan + result = rpc.diceaddfunds(dicename,diceid,"-1") + assert_error(result) + + # adding funds to plan + addfundstx = rpc.diceaddfunds(dicename,diceid,"1100") + result = self.send_and_mine(addfundstx['hex'], rpc) + + # checking if funds added to plan + result = rpc.diceinfo(diceid) + assert_equal(result["funding"], "2100.00000000") + + # not valid dice info checking + result = rpc.diceinfo("invalid") + assert_error(result) + + # placing 0 amount bet + result = rpc1.dicebet(dicename,diceid,"0","2") + assert_error(result) + + # placing negative amount bet + result = rpc1.dicebet(dicename,diceid,"-1","2") + assert_error(result) + + # placing bet more than maxbet + result = rpc1.dicebet(dicename,diceid,"900","2") + assert_error(result) + + # placing bet with amount more than funding + result = rpc1.dicebet(dicename,diceid,"3000","2") + assert_error(result) + + # placing bet with potential won more than funding + result = rpc1.dicebet(dicename,diceid,"750","9") + assert_error(result) + + # placing 0 odds bet + result = rpc1.dicebet(dicename,diceid,"1","0") + assert_error(result) + + # placing negative odds bet + result = rpc1.dicebet(dicename,diceid,"1","-1") + assert_error(result) + + # placing bet with odds more than allowed + result = rpc1.dicebet(dicename,diceid,"1","11") + assert_error(result) + + # placing bet with not correct dice name + result = rpc1.dicebet("nope",diceid,"100","2") + assert_error(result) + + # placing bet with not correct dice id + result = rpc1.dicebet(dicename,self.pubkey,"100","2") + assert_error(result) + + # have to make some entropy for the next test + entropytx = 0 + fundingsum = 1 + while entropytx < 110: + fundingsuminput = str(fundingsum) + fundinghex = rpc.diceaddfunds(dicename,diceid,fundingsuminput) + result = self.send_and_mine(fundinghex['hex'], rpc) + entropytx = entropytx + 1 + fundingsum = fundingsum + 1 + + rpc.generate(2) + self.sync_all() + + # valid bet placing + placebet = rpc1.dicebet(dicename,diceid,"100","2") + betid = self.send_and_mine(placebet["hex"], rpc1) + assert result, "bet placed" + + # check bet status + result = rpc1.dicestatus(dicename,diceid,betid) + assert_success(result) + + # note initial dice funding state at this point. + # TODO: track player balance somehow (hard to do because of mining and fees) + diceinfo = rpc.diceinfo(diceid) + funding = float(diceinfo['funding']) + + # # placing same amount bets with amount 1 and odds 1:3, checking if balance changed correct + # losscounter = 0 + # wincounter = 0 + # betcounter = 0 + # + # while (betcounter < 10): + # placebet = rpc1.dicebet(dicename,diceid,"1","2") + # betid = self.send_and_mine(placebet["hex"], rpc1) + # time.sleep(3) + # self.sync_all() + # finish = rpc.dicefinish(dicename,diceid,betid) + # self.send_and_mine(finish["hex"], rpc1) + # self.sync_all() + # time.sleep(3) + # betresult = rpc1.dicestatus(dicename,diceid,betid) + # betcounter = betcounter + 1 + # if betresult["status"] == "loss": + # losscounter = losscounter + 1 + # elif betresult["status"] == "win": + # wincounter = wincounter + 1 + # else: + # pass + # + # # funding balance should increase if player loss, decrease if player won + # fundbalanceguess = funding + losscounter - wincounter * 2 + # fundinfoactual = rpc.diceinfo(diceid) + # assert_equal(round(fundbalanceguess),round(float(fundinfoactual['funding']))) + + def run_token_tests(self): + rpc = self.nodes[0] + result = rpc.tokenaddress() + assert_success(result) + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: + assert_equal(result[x][0], 'R') + + result = rpc.tokenaddress(self.pubkey) + assert_success(result) + for x in ['myCCAddress(Tokens)', 'TokensNormalAddress', 'TokensNormalAddress', 'myaddress','TokensCCAddress']: + assert_equal(result[x][0], 'R') + # there are no tokens created yet + result = rpc.tokenlist() + assert_equal(result, []) + + # trying to create token with negaive supply + result = rpc.tokencreate("NUKE", "-1987420", "no bueno supply") + assert_error(result) + + # creating token with name more than 32 chars + result = rpc.tokencreate("NUKE123456789012345678901234567890", "1987420", "name too long") + assert_error(result) + + # creating valid token + result = rpc.tokencreate("DUKE", "1987.420", "Duke's custom token") + assert_success(result) + + tokenid = self.send_and_mine(result['hex'], rpc) + + result = rpc.tokenlist() + assert_equal(result[0], tokenid) + + # get token balance for token with pubkey + result = rpc.tokenbalance(tokenid, self.pubkey) + assert_success(result) + assert_equal(result['balance'], 198742000000) + assert_equal(result['tokenid'], tokenid) + + # get token balance for token without pubkey + result = rpc.tokenbalance(tokenid) + assert_success(result) + assert_equal(result['balance'], 198742000000) + assert_equal(result['tokenid'], tokenid) + + # this is not a valid assetid + result = rpc.tokeninfo(self.pubkey) + assert_error(result) + + # check tokeninfo for valid token + result = rpc.tokeninfo(tokenid) + assert_success(result) + assert_equal(result['tokenid'], tokenid) + assert_equal(result['owner'], self.pubkey) + assert_equal(result['name'], "DUKE") + assert_equal(result['supply'], 198742000000) + assert_equal(result['description'], "Duke's custom token") + + # invalid numtokens ask + result = rpc.tokenask("-1", tokenid, "1") + assert_error(result) + + # invalid numtokens ask + result = rpc.tokenask("0", tokenid, "1") + assert_error(result) + + # invalid price ask + result = rpc.tokenask("1", tokenid, "-1") + assert_error(result) + + # invalid price ask + result = rpc.tokenask("1", tokenid, "0") + assert_error(result) + + # invalid tokenid ask + result = rpc.tokenask("100", "deadbeef", "1") + assert_error(result) + + # valid ask + tokenask = rpc.tokenask("100", tokenid, "7.77") + tokenaskhex = tokenask['hex'] + tokenaskid = self.send_and_mine(tokenask['hex'], rpc) + result = rpc.tokenorders(tokenid) + order = result[0] + assert order, "found order" + + # invalid ask fillunits + result = rpc.tokenfillask(tokenid, tokenaskid, "0") + assert_error(result) + + # invalid ask fillunits + result = rpc.tokenfillask(tokenid, tokenaskid, "-777") + assert_error(result) + + # valid ask fillunits + fillask = rpc.tokenfillask(tokenid, tokenaskid, "777") + result = self.send_and_mine(fillask['hex'], rpc) + txid = result[0] + assert txid, "found txid" + + # should be no token orders + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # checking ask cancellation + testorder = rpc.tokenask("100", tokenid, "7.77") + testorderid = self.send_and_mine(testorder['hex'], rpc) + cancel = rpc.tokencancelask(tokenid, testorderid) + self.send_and_mine(cancel["hex"], rpc) + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # invalid numtokens bid + result = rpc.tokenbid("-1", tokenid, "1") + assert_error(result) + + # invalid numtokens bid + result = rpc.tokenbid("0", tokenid, "1") + assert_error(result) + + # invalid price bid + result = rpc.tokenbid("1", tokenid, "-1") + assert_error(result) + + # invalid price bid + result = rpc.tokenbid("1", tokenid, "0") + assert_error(result) + + # invalid tokenid bid + result = rpc.tokenbid("100", "deadbeef", "1") + assert_error(result) + + tokenbid = rpc.tokenbid("100", tokenid, "10") + tokenbidhex = tokenbid['hex'] + tokenbidid = self.send_and_mine(tokenbid['hex'], rpc) + result = rpc.tokenorders(tokenid) + order = result[0] + assert order, "found order" + + # invalid bid fillunits + result = rpc.tokenfillbid(tokenid, tokenbidid, "0") + assert_error(result) + + # invalid bid fillunits + result = rpc.tokenfillbid(tokenid, tokenbidid, "-777") + assert_error(result) + + # valid bid fillunits + fillbid = rpc.tokenfillbid(tokenid, tokenbidid, "1000") + result = self.send_and_mine(fillbid['hex'], rpc) + txid = result[0] + assert txid, "found txid" + + # should be no token orders + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # checking bid cancellation + testorder = rpc.tokenbid("100", tokenid, "7.77") + testorderid = self.send_and_mine(testorder['hex'], rpc) + cancel = rpc.tokencancelbid(tokenid, testorderid) + self.send_and_mine(cancel["hex"], rpc) + result = rpc.tokenorders(tokenid) + assert_equal(result, []) + + # invalid token transfer amount (have to add status to CC code!) + randompubkey = "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96" + result = rpc.tokentransfer(tokenid,randompubkey,"0") + assert_error(result) + + # invalid token transfer amount (have to add status to CC code!) + result = rpc.tokentransfer(tokenid,randompubkey,"-1") + assert_error(result) + + # valid token transfer + sendtokens = rpc.tokentransfer(tokenid,randompubkey,"1") + self.send_and_mine(sendtokens["hex"], rpc) + result = rpc.tokenbalance(tokenid,randompubkey) + assert_equal(result["balance"], 1) + + def run_rewards_tests(self): + rpc = self.nodes[0] + result = rpc.rewardsaddress() + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: + assert_equal(result[x][0], 'R') + + result = rpc.rewardsaddress(self.pubkey) + for x in ['myCCAddress(Rewards)', 'myaddress', 'RewardsCCAddress', 'RewardsCCTokensAddress', 'RewardsNormalAddress']: + assert_equal(result[x][0], 'R') + + # no rewards yet + result = rpc.rewardslist() + assert_equal(result, []) + + # looking up non-existent reward should return error + result = rpc.rewardsinfo("none") + assert_error(result) + + # creating rewards plan with name > 8 chars, should return error + result = rpc.rewardscreatefunding("STUFFSTUFF", "7777", "25", "0", "10", "10") + assert_error(result) + + # creating rewards plan with 0 funding + result = rpc.rewardscreatefunding("STUFF", "0", "25", "0", "10", "10") + assert_error(result) + + # creating rewards plan with 0 maxdays + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "0") + assert_error(result) + + # creating rewards plan with > 25% APR + result = rpc.rewardscreatefunding("STUFF", "7777", "30", "0", "10", "10") + assert_error(result) + + # creating valid rewards plan + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") + assert result['hex'], 'got raw xtn' + fundingtxid = rpc.sendrawtransaction(result['hex']) + assert fundingtxid, 'got txid' + + # confirm the above xtn + rpc.generate(1) + result = rpc.rewardsinfo(fundingtxid) + assert_success(result) + assert_equal(result['name'], 'STUFF') + assert_equal(result['APR'], "25.00000000") + assert_equal(result['minseconds'], 0) + assert_equal(result['maxseconds'], 864000) + assert_equal(result['funding'], "7777.00000000") + assert_equal(result['mindeposit'], "10.00000000") + assert_equal(result['fundingtxid'], fundingtxid) + + # checking if new plan in rewardslist + result = rpc.rewardslist() + assert_equal(result[0], fundingtxid) + + # creating reward plan with already existing name, should return error + result = rpc.rewardscreatefunding("STUFF", "7777", "25", "0", "10", "10") + assert_error(result) + + # add funding amount must be positive + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "-1") + assert_error(result) + + # add funding amount must be positive + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "0") + assert_error(result) + + # adding valid funding + result = rpc.rewardsaddfunding("STUFF", fundingtxid, "555") + addfundingtxid = self.send_and_mine(result['hex'], rpc) + assert addfundingtxid, 'got funding txid' + + # checking if funding added to rewardsplan + result = rpc.rewardsinfo(fundingtxid) + assert_equal(result['funding'], "8332.00000000") + + # trying to lock funds, locking funds amount must be positive + result = rpc.rewardslock("STUFF", fundingtxid, "-5") + assert_error(result) + + # trying to lock funds, locking funds amount must be positive + result = rpc.rewardslock("STUFF", fundingtxid, "0") + assert_error(result) + + # trying to lock less than the min amount is an error + result = rpc.rewardslock("STUFF", fundingtxid, "7") + assert_error(result) + + # locking funds in rewards plan + result = rpc.rewardslock("STUFF", fundingtxid, "10") + assert_success(result) + locktxid = result['hex'] + assert locktxid, "got lock txid" + + # locktxid has not been broadcast yet + result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) + assert_error(result) + + # broadcast xtn + txid = rpc.sendrawtransaction(locktxid) + assert txid, 'got txid from sendrawtransaction' + + # confirm the xtn above + rpc.generate(1) + + # will not unlock since reward amount is less than tx fee + result = rpc.rewardsunlock("STUFF", fundingtxid, locktxid) + assert_error(result) + + def run_oracles_tests(self): + rpc = self.nodes[0] + rpc1 = self.nodes[1] + + result = rpc1.oraclesaddress() + + result = rpc.oraclesaddress() + assert_success(result) + + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + result = rpc.oraclesaddress(self.pubkey) + assert_success(result) + for x in ['OraclesCCAddress', 'OraclesNormalAddress', 'myCCAddress(Oracles)','OraclesCCTokensAddress', 'myaddress']: + assert_equal(result[x][0], 'R') + + # there are no oracles created yet + result = rpc.oracleslist() + assert_equal(result, []) + + # looking up non-existent oracle should return error. + result = rpc.oraclesinfo("none") + assert_error(result) + + # attempt to create oracle with not valid data type should return error + result = rpc.oraclescreate("Test", "Test", "Test") + assert_error(result) + + # attempt to create oracle with description > 32 symbols should return error + too_long_name = generate_random_string(33) + result = rpc.oraclescreate(too_long_name, "Test", "s") + + + # attempt to create oracle with description > 4096 symbols should return error + too_long_description = generate_random_string(4100) + result = rpc.oraclescreate("Test", too_long_description, "s") + assert_error(result) + # # valid creating oracles of different types + # # using such naming to re-use it for data publishing / reading (e.g. oracle_s for s type) + # valid_formats = ["s", "S", "d", "D", "c", "C", "t", "T", "i", "I", "l", "L", "h", "Ihh"] + # for f in valid_formats: + # result = rpc.oraclescreate("Test", "Test", f) + # assert_success(result) + # globals()["oracle_{}".format(f)] = self.send_and_mine(result['hex'], rpc) + + def run_test (self): + print("Mining blocks...") + rpc = self.nodes[0] + rpc1 = self.nodes[1] + # utxos from block 1 become mature in block 101 + rpc.generate(101) + self.sync_all() + rpc.getinfo() + rpc1.getinfo() + # this corresponds to -pubkey above + print("Importing privkeys") + rpc.importprivkey(self.privkey) + rpc1.importprivkey(self.privkey1) + self.run_faucet_tests() + self.sync_all() + self.run_rewards_tests() + self.sync_all() + self.run_dice_tests() + self.sync_all() + self.run_token_tests() + self.sync_all() + self.run_oracles_tests() + + +if __name__ == '__main__': + CryptoConditionsTest ().main() diff --git a/src/bitcoind.cpp b/src/bitcoind.cpp index b65fcf1a9..4e010d28e 100644 --- a/src/bitcoind.cpp +++ b/src/bitcoind.cpp @@ -64,7 +64,7 @@ extern uint64_t ASSETCHAINS_CBOPRET; void komodo_passport_iteration(); uint64_t komodo_interestsum(); int32_t komodo_longestchain(); -void komodo_cbopretupdate(); +void komodo_cbopretupdate(int32_t forceflag); void WaitForShutdown(boost::thread_group* threadGroup) { @@ -89,7 +89,7 @@ void WaitForShutdown(boost::thread_group* threadGroup) //komodo_interestsum(); //komodo_longestchain(); if ( ASSETCHAINS_CBOPRET != 0 ) - komodo_cbopretupdate(); + komodo_cbopretupdate(0); for (i=0; i<=ASSETCHAINS_BLOCKTIME/5; i++) { fShutdown = ShutdownRequested(); diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 1a68be7dd..1ecaebb2c 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -18,17 +18,33 @@ #define CC_PRICES_H #include "CCinclude.h" +int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind); + +#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +#define PRICES_TXFEE 10000 +#define PRICES_MAXLEVERAGE 777 +#define PRICES_SMOOTHWIDTH 1 +#define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 +#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) +#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) +#define PRICES_MULT (KOMODO_MAXPRICES * 2) +#define PRICES_DIV (KOMODO_MAXPRICES * 3) +#define PRICES_INV (KOMODO_MAXPRICES * 4) +#define PRICES_MDD (KOMODO_MAXPRICES * 5) +#define PRICES_MMD (KOMODO_MAXPRICES * 6) +#define PRICES_MMM (KOMODO_MAXPRICES * 7) +#define PRICES_DDD (KOMODO_MAXPRICES * 8) bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom +UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); +UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount); +UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid); +UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight); +UniValue PricesCashout(uint64_t txfee,uint256 bettxid); +UniValue PricesInfo(uint256 bettxid,int32_t refheight); UniValue PricesList(); -UniValue PricesInfo(uint256 fundingtxid); -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid); -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys); -std::string PricesAddFunding(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount); -std::string PricesBet(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,int64_t amount,int32_t leverage); -std::string PricesFinish(uint64_t txfee,uint256 bettoken,uint256 fundingtxid,uint256 bettxid); #endif diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index 00a7c6fe0..8552cfa1b 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -195,7 +195,7 @@ cJSON *cclib_reparse(int32_t *nump,char *jsonstr) // assumes origparams will be UniValue CClib_method(struct CCcontract_info *cp,char *method,char *jsonstr) { UniValue result(UniValue::VOBJ); uint64_t txfee = 10000; int32_t m; cJSON *params = cclib_reparse(&m,jsonstr); - //fprintf(stderr,"method.(%s) -> (%s)\n",jsonstr!=0?jsonstr:"",params!=0?jprint(params,0):""); + fprintf(stderr,"method.(%s) -> (%s)\n",jsonstr!=0?jsonstr:"",params!=0?jprint(params,0):""); #ifdef BUILD_ROGUE if ( cp->evalcode == EVAL_ROGUE ) { @@ -351,7 +351,7 @@ UniValue CClib_info(struct CCcontract_info *cp) UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr) { UniValue result(UniValue::VOBJ); int32_t i; std::string rawtx; cJSON *params; - //printf("CClib params.(%s)\n",jsonstr!=0?jsonstr:""); +//printf("CClib params.(%s)\n",jsonstr!=0?jsonstr:""); for (i=0; ievalcode == CClib_methods[i].evalcode && strcmp(method,CClib_methods[i].method) == 0 ) @@ -369,7 +369,7 @@ UniValue CClib(struct CCcontract_info *cp,char *method,char *jsonstr) } } result.push_back(Pair("result","error")); - result.push_back(Pair("method",CClib_methods[i].method)); + result.push_back(Pair("method",method)); result.push_back(Pair("error","method not found")); return(result); } diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index 504ba629a..2887c18e1 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -62,6 +62,8 @@ Possible third iteration: // start of consensus code +#define CC_MARKER_VALUE 10000 + int64_t IsChannelsvout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey srcpub, CPubKey destpub,int32_t v) { char destaddr[65],channeladdr[65],tokenschanneladdr[65]; @@ -126,8 +128,8 @@ uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint2 { return(f); } - } else fprintf(stderr,"script[0] %02x != EVAL_CHANNELS\n",script[0]); - } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "script[0] " << script[0] << " != EVAL_CHANNELS" << std::endl); + } else LOGSTREAM("channelscc",CCLOG_DEBUG1, stream << "not enough opret.[" << vopret.size() << "]" << std::endl); return(0); } @@ -167,7 +169,7 @@ bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti } if ( inputs != outputs ) { - fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs); + LOGSTREAM("channelscc",CCLOG_INFO, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl); return eval->Invalid("mismatched inputs != outputs"); } else return (true); @@ -183,7 +185,7 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numpayments,p1,param1; bool retval; uint256 txid,hashblock,p3,param3,opentxid,tmp_txid,genhashchain,hashchain,tokenid; - uint8_t funcid,hash[32],hashdest[32]; + uint8_t funcid,hash[32],hashdest[32]; char channeladdress[65],srcmarker[65],destmarker[65],destaddr[65],srcaddr[65],desttokensaddr[65],srctokensaddr[65]; int64_t p2,param2,payment; CPubKey srcpub, destpub; CTransaction channelOpenTx,channelCloseTx,prevTx; @@ -202,9 +204,20 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & else { txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); + memcpy(hash,&txid,sizeof(hash)); if ( (funcid = DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, opentxid, srcpub, destpub, param1, param2, param3)) != 0) { + if (myGetTransaction(opentxid,channelOpenTx,hashblock)== 0) + return eval->Invalid("invalid channelopen tx!"); + else if ((numvouts=channelOpenTx.vout.size()) > 0 && (DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 'O') + return eval->Invalid("invalid channelopen OP_RETURN data!"); + GetCCaddress1of2(cp,channeladdress,srcpub,destpub); + GetCCaddress(cp,srcmarker,srcpub); + GetCCaddress(cp,destmarker,destpub); + Getscriptaddress(srcaddr,CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG); + Getscriptaddress(destaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); + _GetCCaddress(srctokensaddr,EVAL_TOKENS,srcpub); + _GetCCaddress(desttokensaddr,EVAL_TOKENS,destpub); switch ( funcid ) { case 'O': @@ -225,56 +238,53 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.3: normal output of payment amount to receiver pubkey //vout.n-2: normal change //vout.n-1: opreturn - 'P' opentxid senderspubkey receiverspubkey depth numpayments secret - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelPayment!"); + return eval->Invalid("vin.0 is normal for channelpayment!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelPayment!"); + return eval->Invalid("vin.1 is CC for channelpayment!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelPayment!"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition()==0 ) - return eval->Invalid("vout.0 is CC for channelPayment!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelPayment (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,2)==0 ) - return eval->Invalid("vout.2 is CC for channelPayment (marker to dstPub)!"); - else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.3 is CC for channelPayment!"); - else if ( tokenid==zeroid && tx.vout[3].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("vout.3 is normal for channelPayment!"); - else if ( tokenid!=zeroid && tx.vout[3].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[3].nValue,destpub).scriptPubKey) - return eval->Invalid("payment funds do not go to receiver!"); - else if ( tokenid==zeroid && tx.vout[3].scriptPubKey!=CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG) - return eval->Invalid("payment funds do not go to receiver!"); + return eval->Invalid("vin.2 is CC for channelpayment!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelpayment!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelpayment!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[3],1,desttokensaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is CC or invalid amount or invalid receiver for channelpayment!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[3],0,destaddr,param2*payment)==0 ) + return eval->Invalid("vout.3 is normal or invalid amount or invalid receiver for channelpayment!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); else - { - if (myGetTransaction(opentxid,channelOpenTx,hashblock) != 0) + { + endiancpy(hash, (uint8_t * ) & param3, 32); + for (i = 0; i < numpayments-param1; i++) { - if ((numvouts=channelOpenTx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain)) != 0 && funcid!='O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - endiancpy(hash, (uint8_t * ) & param3, 32); - for (i = 0; i < numpayments-param1; i++) - { - vcalc_sha256(0, hashdest, hash, 32); - memcpy(hash, hashdest, 32); - } - endiancpy((uint8_t*)&genhashchain,hashdest,32); - if (hashchain!=genhashchain) - return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); - else if (tx.vout[3].nValue != param2*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); + vcalc_sha256(0, hashdest, hash, 32); + memcpy(hash, hashdest, 32); } + endiancpy((uint8_t*)&genhashchain,hashdest,32); + if (hashchain!=genhashchain) + return eval->Invalid("invalid secret for payment, does not reach final hashchain!"); + else if (tx.vout[3].nValue != param2*payment) + return eval->Invalid("vout amount does not match number_of_payments*payment!"); if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,(p1-param2)*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid CC change amount for channelpayment!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelpayment!"); else if (param1+param2!=p1) return eval->Invalid("invalid payment depth!"); else if (tx.vout[3].nValue > prevTx.vout[0].nValue) @@ -290,37 +300,37 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.1: CC vout marker to senders pubKey //vout.2: CC vout marker to receiver pubkey //vout.n-2: normal change - //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey 0 0 0 - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + //vout.n-1: opreturn - 'C' opentxid senderspubkey receiverspubkey numpayments payment 0 + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelClose!"); + return eval->Invalid("vin.0 is normal for channelclose!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelClose!"); + return eval->Invalid("vin.1 is CC for channelclose!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelClose!"); - else if ( IsChannelsvout(cp,tx,srcpub,destpub,0)==0 ) - return eval->Invalid("vout.0 is CC for channelClose!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelClose (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,2)==0 ) - return eval->Invalid("vout.2 is CC for channelClose (marker to dstPub)!"); + return eval->Invalid("vin.2 is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[0],1,channeladdress,0)==0 ) + return eval->Invalid("vout.0 is CC for channelclose!"); + else if ( ConstrainVout(tx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelclose!"); + else if ( ConstrainVout(tx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelclose!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); - else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - return eval->Invalid("invalid open txid!"); - else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - else if (tx.vout[0].nValue != param1*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[2].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelclose!"); else if (tx.vout[0].nValue != prevTx.vout[0].nValue) return eval->Invalid("invalid CC amount, amount must match funds in channel"); } @@ -334,50 +344,40 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & //vout.2: normal output of CC input to senders pubkey //vout.n-2: normal change //vout.n-1: opreturn - 'R' opentxid senderspubkey receiverspubkey numpayments payment closetxid - if (komodo_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelOpen is not yet confirmed(notarised)!"); + if ( IsCCInput(channelOpenTx.vin[0].scriptSig) != 0 ) + return eval->Invalid("vin.0 is normal for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[0],1,channeladdress,numpayments*payment)==0 ) + return eval->Invalid("vout.0 is CC or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[1],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to srcpub or invalid amount for channelopen!"); + else if ( ConstrainVout(channelOpenTx.vout[2],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.2 is CC marker to destpub or invalid amount for channelopen!"); + else if (komodo_txnotarizedconfirmed(opentxid) == 0) + return eval->Invalid("channelopen is not yet confirmed(notarised)!"); else if (komodo_txnotarizedconfirmed(param3) == 0) return eval->Invalid("channelClose is not yet confirmed(notarised)!"); else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelRefund!"); + return eval->Invalid("vin.0 is normal for channelrefund!"); else if ( IsCCInput(tx.vin[1].scriptSig) == 0 ) - return eval->Invalid("vin.1 is CC for channelRefund!"); + return eval->Invalid("vin.1 is CC for channelrefund!"); else if ( IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin.2 is CC for channelRefund!"); - else if ( IsChannelsMarkervout(cp,tx,srcpub,0)==0 ) - return eval->Invalid("vout.0 is CC for channelRefund (marker to srcPub)!"); - else if ( IsChannelsMarkervout(cp,tx,destpub,1)==0 ) - return eval->Invalid("vout.1 is CC for channelRefund (marker to dstPub)!"); - else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.2 is CC for channelPayment!"); - else if ( tokenid==zeroid && tx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 ) - return eval->Invalid("vout.2 is normal for channelPayment!"); - else if ( tokenid!=zeroid && tx.vout[2].scriptPubKey!=MakeCC1vout(EVAL_TOKENS,tx.vout[2].nValue,srcpub).scriptPubKey) - return eval->Invalid("payment funds do not go to sender!"); - else if ( tokenid==zeroid && tx.vout[2].scriptPubKey!=CScript() << ParseHex(HexStr(srcpub)) << OP_CHECKSIG) - return eval->Invalid("payment funds do not go to sender!"); + return eval->Invalid("vin.2 is CC for channelrefund!"); + else if ( ConstrainVout(tx.vout[0],1,srcmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.0 is CC marker to srcpub or invalid amount for channelrefund!"); + else if ( ConstrainVout(tx.vout[1],1,destmarker,CC_MARKER_VALUE)==0 ) + return eval->Invalid("vout.1 is CC marker to destpub or invalid amount for channelrefund!"); + else if ( tokenid!=zeroid && ConstrainVout(tx.vout[2],1,srctokensaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is CC or invalid amount or invalid receiver for channelrefund!"); + else if ( tokenid==zeroid && ConstrainVout(tx.vout[2],0,srcaddr,param1*payment)==0 ) + return eval->Invalid("vout.2 is normal or invalid amount or invalid receiver for channelrefund!"); else if ( param1 > CHANNELS_MAXPAYMENTS) return eval->Invalid("too many payment increments!"); - else if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - return eval->Invalid("invalid open txid!"); - else if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, numpayments, payment, hashchain) != 'O') - return eval->Invalid("invalid channelopen OP_RETURN data!"); - else if (myGetTransaction(param3,channelCloseTx,hashblock) == 0) - return eval->Invalid("invalid close txid!"); - else if ((numvouts=channelCloseTx.vout.size()) > 0 && DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, param1, param2, param3) != 'C') - return eval->Invalid("invalid channelclose OP_RETURN data!"); - else if (tmp_txid!=opentxid) - return eval->Invalid("invalid close tx, opentxid do not match on close and refund!"); - else if (tx.vout[2].nValue != param1*payment) - return eval->Invalid("vout amount does not match number_of_payments*payment!"); else if (myGetTransaction(tx.vin[1].prevout.hash,prevTx,hashblock) != 0) { if ((numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, tmp_txid, srcpub, destpub, p1, p2, p3) == 0) return eval->Invalid("invalid previous tx OP_RETURN data!"); - else if (tx.vout[0].scriptPubKey != prevTx.vout[1].scriptPubKey) - return eval->Invalid("invalid destination for sender marker!"); - else if (tx.vout[1].scriptPubKey != prevTx.vout[2].scriptPubKey) - return eval->Invalid("invalid destination for receiver marker!"); + else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || prevTx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE) + return eval->Invalid("vin.2 is CC marker or invalid marker amount for channelrefund!"); else if (tx.vout[2].nValue != prevTx.vout[0].nValue) return eval->Invalid("invalid amount, refund amount and funds in channel must match!"); } @@ -390,8 +390,8 @@ bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction & else return eval->Invalid("unexpected channels missing funcid"); retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) - fprintf(stderr,"Channel tx validated\n"); - else fprintf(stderr,"Channel tx invalid\n"); + LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl); + else fprintf(stderr,"Channels tx invalid\n"); return(retval); } } @@ -415,7 +415,7 @@ int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, C } else { - fprintf(stderr,"invalid channel open txid\n"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << "invalid channel open txid" << std::endl); return 0; } if (srcpub==mypk) marker=1; @@ -473,7 +473,7 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS ) { CCerror = strprintf("invalid ChannelOpen param numpayments.%d max.%d payment.%lld\n",numpayments,CHANNELS_MAXPAYMENTS,(long long)payment); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } cp = CCinit(&C,EVAL_CHANNELS); @@ -484,11 +484,11 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 funds = numpayments * payment; if (tokenid!=zeroid) { - amount=AddNormalinputs(mtx,mypk,3*txfee,5); + amount=AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,5); tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64); } - else amount=AddNormalinputs(mtx,mypk,funds+3*txfee,64); - if (amount+tokens >= funds+2*txfee) + else amount=AddNormalinputs(mtx,mypk,funds+txfee+2*CC_MARKER_VALUE,64); + if (amount+tokens >= funds+txfee+2*CC_MARKER_VALUE) { hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); endiancpy(hash,(uint8_t *)&hentropy,32); @@ -500,13 +500,13 @@ std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64 endiancpy((uint8_t *)&hashchain,hashdest,32); if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid && tokens>funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain))); } CCerror = strprintf("error adding funds"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -526,7 +526,7 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O') @@ -534,23 +534,29 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (mypk != srcpub && mypk != destpub) { CCerror = strprintf("this is not our channel"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } else if (amount % payment != 0 || amount 0) + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } + if (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0) { @@ -562,12 +568,12 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (numpayments > prevdepth) { CCerror = strprintf("not enough funds in channel for that amount"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } else if (numpayments == 0) { CCerror = strprintf("invalid amount"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (secret!=zeroid) @@ -582,7 +588,7 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 if (gensecret!=hashchain) { CCerror = strprintf("invalid secret supplied"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } @@ -605,13 +611,13 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 else { CCerror = strprintf("invalid previous tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, change, srcpub, destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,srcpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,srcpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, amount, destpub)); else mtx.vout.push_back(CTxOut(amount, CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG)); return (FinalizeCCTx(0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret))); @@ -619,12 +625,12 @@ std::string ChannelPayment(uint64_t txfee,uint256 opentxid,int64_t amount, uint2 else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -645,40 +651,46 @@ std::string ChannelClose(uint64_t txfee,uint256 opentxid) if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { CCerror = strprintf("invalid channel open tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (mypk != srcpub) { CCerror = strprintf("cannot close, you are not channel owner"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS, funds, mypk, destpub)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('C',tokenid,opentxid,mypk,destpub,funds/payment,payment,zeroid))); } else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -699,48 +711,60 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) if (GetTransaction(closetxid,channelCloseTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel close txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C') { CCerror = strprintf("invalid channel close tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(closetxid)==false) + { + CCerror = strprintf("channelsclose tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if (txid!=opentxid) { CCerror = strprintf("open and close txid are not from same channel"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (GetTransaction(opentxid,channelOpenTx,hashblock,false) == 0) { CCerror = strprintf("invalid channel open txid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } + if (komodo_txnotarizedconfirmed(opentxid)==false) + { + CCerror = strprintf("channelsopen tx not yet confirmed/notarized"); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); + return(""); + } if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') { CCerror = strprintf("invalid channel open tx"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return (""); } if (mypk != srcpub) { CCerror = strprintf("cannot refund, you are not the channel owner"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3) > 0 ) { if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) { if ((GetTransaction(prevtxid,prevTx,hashblock,false) != 0) && (numvouts=prevTx.vout.size()) > 0 && DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3) != 0) { - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,mypk)); - mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,txfee,destpub)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk)); + mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub)); if (tokenid!=zeroid) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,funds,mypk)); else mtx.vout.push_back(CTxOut(funds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('R',tokenid,opentxid,mypk,destpub,funds/payment,payment,closetxid))); @@ -748,19 +772,19 @@ std::string ChannelRefund(uint64_t txfee,uint256 opentxid,uint256 closetxid) else { CCerror = strprintf("previous tx is invalid"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } else { CCerror = strprintf("error adding CC inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } } CCerror = strprintf("error adding normal inputs"); - fprintf(stderr,"%s\n",CCerror.c_str()); + LOGSTREAM("channelscc",CCLOG_INFO, stream << CCerror << std::endl); return(""); } @@ -781,7 +805,7 @@ UniValue ChannelsList() txid = it->first.txhash; vout = (int32_t)it->first.index; nValue = (int64_t)it->second; - if ( (vout == 1 || vout == 2) && nValue == 10000 && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) + if ( (vout == 1 || vout == 2) && nValue == CC_MARKER_VALUE && GetTransaction(txid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 ) { if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O') { diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index e0c4c8ada..ae83e2661 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -732,6 +732,78 @@ UniValue PaymentsCreate(struct CCcontract_info *cp,char *jsonstr) return(result); } +UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) +{ + // need to code: exclude list of tokenid, dust threshold, maxpayees, excluded pubkeys[] + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); CTransaction tx; CPubKey Paymentspk,mypk; char markeraddr[64]; std::vector txidoprets; uint256 hashBlock; int32_t i,n,numoprets=0,lockedblocks,minrelease; std::string rawtx; int64_t totalallocations = 0; + cJSON *params = payments_reparse(&n,jsonstr); + if ( params != 0 && n >= 4 ) + { + lockedblocks = juint(jitem(params,0),0); + minrelease = juint(jitem(params,1),0); + if ( lockedblocks < 0 || minrelease < 0 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","negative parameter")); + if ( params != 0 ) + free_json(params); + return(result); + } + for (i=0; i scriptPubKey,opret; int64_t allocation; + if ( myGetTransaction(txidoprets[i],tx,hashBlock) != 0 && tx.vout.size() > 1 && DecodePaymentsTxidOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,allocation,scriptPubKey,opret) == 'T' ) + { + totalallocations += allocation; + if ( opret.size() > 0 ) + numoprets++; + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid txidopret")); + result.push_back(Pair("txid",txidoprets[i].GetHex())); + result.push_back(Pair("txi",(int64_t)i)); + if ( params != 0 ) + free_json(params); + return(result); + } + } + if ( numoprets > 1 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","too many opreturns")); + result.push_back(Pair("numoprets",(int64_t)numoprets)); + if ( params != 0 ) + free_json(params); + return(result); + } + mypk = pubkey2pk(Mypubkey()); + Paymentspk = GetUnspendable(cp,0); + if ( AddNormalinputs(mtx,mypk,2*PAYMENTS_TXFEE,60) > 0 ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,PAYMENTS_TXFEE,Paymentspk,Paymentspk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,PAYMENTS_TXFEE,EncodePaymentsOpRet(lockedblocks,minrelease,totalallocations,txidoprets)); + if ( params != 0 ) + free_json(params); + return(payments_rawtxresult(result,rawtx,1)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough normal funds")); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","parameters error")); + } + if ( params != 0 ) + free_json(params); + return(result); +} + UniValue PaymentsInfo(struct CCcontract_info *cp,char *jsonstr) { UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,txO; CPubKey Paymentspk,txidpk; int32_t i,j,n,flag=0,numoprets=0,lockedblocks,minrelease; std::vector txidoprets; int64_t funds,fundsopret,totalallocations=0,allocation; char fundsaddr[64],fundsopretaddr[64],txidaddr[64],*outstr; uint256 createtxid,hashBlock; diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index c435d9920..341e34c58 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -16,15 +16,7 @@ #include "CCPegs.h" /* - Pegs CC builds on top of Prices CC and would bind a pricefeed to a token bid/ask automated marketmaking. - - Funds deposited into CC address for a specific peg would then be used to fund the bid/ask as the pricefeed changes the price. Profits/losses would accumulate in the associated address. - - In the event funds exceed a specified level, it can be spent into a collection address. The idea is that the collection address can further be used for revshares. - - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); - OraclePrice is very useful for pegs. */ diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 1ab5aa1cb..ce257d33f 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -17,123 +17,214 @@ #include "CCPrices.h" /* - Prices CC would best build on top of the oracles CC, ie. to combine payments for multiple oracles and to calculate a 51% protected price feed. +CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; - We need to assume there is an oracle for a specific price. In the event there are more than one provider, the majority need to be within correlation distance to update a pricepoint. + 0.5% fee based on betamount, NOT leveraged betamount!! + 0.1% collected by price basis determinant + 0.2% collected by rekt tx - int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); + At the simplest, prices CC allows to make leveraged cash settled bets on long and short BTCUSD: + BTCUSD, 1 with positive leverage parameter up to 777 + BTCUSD, 1 with negative leverage parameter up to -777 + + These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define + + Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection address (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) + + The definition of the synthetic instrument that is being tracked is actually defined with a forth type of syntax and is quite flexible to allow unlimited combinations and weights, but that is an independent aspect and will be covered later. + + Let us discuss the simplest synthetic case of a long or short of BTCUSD (or any other direct pricepoint from the trustless oracle). If you look at the charts, you will see the blue line that is direct from the miners (and therefore cant be directly used). The orange line is the 51% correlated price that is deterministically random selected from the prior 24 hours. And finally the green line which is simply the average value from the priot 24 hours. + + We will assume that the orange and green prices are able to be deterministically calculated from the raw coinbase data (blue). The prices rpc is currently working reasonably well and appears to return deterministic prices, however for use in the prices CC, it is often needed to find just a single data point. To that effect there is the temporary function prices_syntheticprice, which uses a bruteforce and totally inefficient way to calculate the correlated and smoothed prices. This inefficient process will need to be changed to a permanent storage of correlated and smoothed prices for each trustless oracle that is updated each block. This will then allow to directly lookup each value for any of the trustless prices. Such speed will indeed be needed to scale up usage of a REKT chain. + + Since the mined prices can be manipulated by any miner, combined with the +/-1% tolerance, it would be possible for an external miner to obtain a large hashrate and skew the price +1%, then to -1% to create a 2% price difference, which under high leverage might be able to generate a large profit. + + To avoid this precisely controllable biasing, the 51% correlation of past day is used (orange), which makes the price jump around a lot. The green line that sums the prior day is needed to remove this jitter back to the average value. However, this creates a 1.5 day delay to the price movement of the smoothed price compared to the current price. What this means is that if we want to use the green line to settle prices automatically, a trivial way to make money is to bet in the direct that the mined prices are relative to the smoothed prices. Given 1.5 day head start, it wont be any mystery which direction the smoothed line will move in the next 24 hours. + + So this makes the smoothed price unusable for settling the bets with. However, we cant use the correlated price either, as it would also allow to make bets when the correlated price picked a significantly lower/higher price than the raw price. The solution is to have a floating basis for the costbasis of the bet. In order to allow finding the right costbasis, for long bets the maximum price between the correlated and smoothed price is needed over the first day after the bet. For short bets, the minimum price is needed. + + What this means is that the actual starting price for a bet wont be known for sure when the bet is made, but if the price is relatively stable, there wont be much of a change. If there is a lot of recent volatility, then the starting price will have a high variability. In order to calculate the costbasis, it currently requires many calculations to find the MAX (or MIN) for the first day. Even when this is made to be a lookup, it still requires to issue a transaction to change the state of a bet from a "starting" state to a "in effect" state and this state change is indicated by whether the bettx vout that contains the costbasis utxo is spent or not. + + Once a bet goes into effect, then block by block, it is checked by the decentralized set of rekt scanners if it is rekt or not, in this case for double the reward for calculating the cost basis. This fully decentralized mechanism ensures that some node will decide to collect the free money and ensures that the bankroll is growing. To miss a rekt bet and not close it out when it can be is to allow it to survive for another block, which can change it profitability from negative to positive. + + Which comes to how profits are calculated. Once the costbasis is locked, then the profit calculation can be made based on the ratio between the costbasis and the current price, multiplied by the leverage. So, if a long position gains 10% at a 10x leverage, then approximately the entire bet amount will be made, ie. a double. Similarily with a short position, a 10% drop in price at 10x leverage will double the bet amount. Since it takes a full day to establish the costbasis, it is not possible to allow changing the costbasis for an existing bet, however it is possible to add funds so that it is less likely to be rekt. The sum of the initial bet and all added funds must be greater than the loss at every block to prevent a position from being rekt. To make it simple to calculate the amount of funds added, another bettx vout is used as a baton. Techniques similar to rogue CC to find where the bettx vout was spent and looking at each such transaction to find the total funds added can be used. + + The once that makes the bet is able to add funds or cashout at any block, as long as it isnt rekt. If it is rekt, the bettor is able to collect the rekt fee, so at least that is some consolation, but it is advised to increase the total funding to avoid being rekt. On a cashout all the funds that were bet adjusted by the profits can be collected. + + Hopefully the above description makes it clear what the following rpc calls should be doing: + + UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) + funds are locked into 1of2 global CC address + for first day, long basis is MAX(correlated,smoothed), short is MIN() + reference price is the smoothed of the previous block + if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price + original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund + 0.5% of bet -> globalfund - Using the above function, a consensus price can be obtained for a datasource. + UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) + add funding to an existing bet, doesnt change the profit calcs but does make the bet less likely to be rekt - given an oracletxid, the marketaddr and format can be extracted to be used for future calls to OraclePrice. This allows to set a starting price and that in turn allows cash settled leveraged trading! + UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) + in the first day from the bet, the costbasis can (and usually does) change based on the MAX(correlated,smoothed) for long and MIN() for shorts + to calculate this requires a bit of work, so whatever node is first to get the proper calculation confirmed, gets a 0.1% costbasis fee - Funds work like with dice, ie. there is a Prices plan that traders bet against. + UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) + similarily, any node can submit a rekt tx and cash in on 0.2% fee if their claim is confirmed - PricesFunding oracletxid, margin, priceaveraging, maxleverage, funding, longtoken, shorttoken, N [pubkeys] + UniValue PricesCashout(uint64_t txfee,uint256 bettxid) + only the actually creator of bet is able to cashout and only if it isnt rekt at that moment - PricesBet -> oracletxid start with 'L', leverage, funding, direction - funds are locked into global CC address - it can be closed at anytime by the trader for cash settlement - the house account can close it if rekt + UniValue PricesInfo(uint256 bettxid,int32_t refheight) + all relevant info about a bet - Implementation Notes: - In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. + UniValue PricesList() + a list of all pending and completed bets in different lists - delta neutral balancing of riskexposure: fabs(long exposure - short exposure) - bet +B at leverage L - absval(sum(+BLi) - sum(-Bli)) - validate: update riskexposure and it needs to be <= funds + Appendix Synthetic position definition language: - PricesProfits: limit withdraw to funds in excess of riskexposure - PricesFinish: payout (if winning) and update riskexposure - need long/short exposure assets + Let us start from the familiar BTCUSD nomenclature. this is similar (identical) to forex EURUSD and the equivalent. Notice the price needs two things as it is actually a ratio, ie. BTCUSD is how many USD does 1 BTC get converted to. It can be thought of as the value of BTC/USD, in other words a ratio. - funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address - pricebet -> user funds and exposure token to 1of2 address. - pricewin -> winnings from dealer funds, exposure token back to global address - priceloss -> exposuretoken back to global address + The value of BTC alone, or USD alone, it is actually quite an abstract issue and it can only be approximated. Specific ways of how to do this can be discussed later, for now it is only important to understand that all prices are actually ratios. - exposure address, funds address + And these ratios work as normal algebra does. So a/b * b/c == a/c! You can try this out with BTCUSD and USDJPY -> BTC/USD * USD/JPY -> BTC/JPY or the BTCJPY price + division is the reciprocal, so BTCUSD reciprocated is USDBTC + + Without getting into all the possible combinations of things, let us first understand what the language allows. It uses a stack based language, where individual tokens update the state. The final state needs to have an empty stack and it will have a calculated price. + + The most important is pushing a specific price onto the stack. All the correlated and smoothed prices are normalized so it has each integer unit equal to 0.00000001, amounts above 100 million are above one, amounts less are below one. 100 million is the unit constant. + + In the prices CC synthetic definition language, the symbol that is returned pushes the adjusted price to the stack. The adjustment is selecting between the correlated and smoothed if within a day from the bet creation, or just the smoothed if after a day. You can have a maximum depth of 3, any more than that and it should return an error. + + This means there are operations that are possible on 1, 2 and 3 symbols. For 1 symbol, it is easy, the only direct operation is the inverse, which is represented by "!". All items in the language are separated by "," + + "BTCUSD, !" + + The above is the inverse or USD/BTC, which is another way to short BTCUSD. It is also possible to short the entire synthetic with a negative leverage. The exact results of a -1 leverage and inverse, might not be exact due to the math involved with calculating the profit, but it should generally be similar. + + For two symbols, there is a bit more we can do, namely multiply and divide, combined with inverting, however to simplify the language any inverting is required to be done by the ordering of the symbols and usage of multiply or divide. multiply is "*" and divide is "/" the top of the stack (last to be pushed) is the divisor, the one right before the divisor is the numerator. + + "BTCUSD, USDJPY, *" <- That will create a BTCJPY synthetic + + "BTCEUR, BTCUSD, /" <- That will create a USDEUR synthetic + + If you experiment around with this, you will see that given two symbols and the proper order and * or /, you can always create the synthetic that you want, assuming what you want is the cancelling out of the term in common with the two symbols and the resulting ratio is between the two unique terms. + */ +// Now we get to the three symbol operations, which there are 4 of *//, **/, *** and /// -*/ +/* + these four operators work on the top of the stack in left to right order as the syntax of the definition lists it, though it is even possible to have the value from an earlier computation on the top of the stack. Ultimately all three will be multiplied together, so a * in a spot means it us used without changing. A / means its inverse will be used. + + "KMDBTC, BTCUSD, USDJPY, ***" <- this would create a KMDJPY synthetic. The various location of the / to make an inverse is to orient the raw symbol into the right orientation as the pricefeed is only getting one orientation of the ratio. + + So now we have covered all ways to map 1, 2 and 3 values on the top of the stack. A value can be on the top of the stack directly from a symbol, or as the result of some 1, 2 or 3 symbol operation. With a maximum stack depth of 3, it might require some experimentation to get a very complex synthetic to compile. Alternately, a stack deeper than 3 might be the acceptable solution if there are a family of synthetics that need it. + + At this point, it is time to describe the weights. It turns out that all above examples are incomplete and the synthetic descriptions are all insufficient and should generate an error. The reason is that the actual synthetic price is the value of the accumulator, which adds up all the weighted prices. In order to assign a weight to a price value on the stack, you simply use a number that is less than 2048. + + What such a weight number does, is consume the top of the stack, which also must be at depth of 1 and adds it to the accumulator. This allows combining multiple different synthetics into a meta synthetic, like for an aggregated index that is weighted by marketcap, or whatever other type of ratio trade that is desired. + + "BTCUSD, 1000, ETHBTC, BTCUSD, *, 300" -> that creates a dual index of BTCUSD and ETHUSD with a 30% ETH weight + + all weight operations consumes the one and only stack element and adds its weight to the accumulator, using this a very large number of terms can be all added together. Using inverses, allows to get the short direction into the equation mixed with longs, but all terms must be positive. If you want to create a spread between two synthetics, you need to create two different synthetics and go long with one and short with another. + + "BTCUSD, 1" with leverage -2 and "KMDBTC, BTCUSD, *, 1" with leverage 1 this will setup a +KMDUSD -2 BTCUSD spread when the two are combined, and would be at breakeven when KMDUSD gains 2x more percentage wise than BTC does. anytime KMD gains more, will be positive, if the gains are less, would be negative. + + Believe it or not, the string to binary compiler for synthetic description and interpretation of it is less than 200 lines of code. + + todo: complete all the above, bet tokens, cross chain prices within cluster These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define + + Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection scriptPubKey (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) this requires adding new vouts + + Modification: in the event there is one price in the accumulator and one price on the stack at the end, then it is a (A - B) spread + */ + // start of consensus code -int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) -{ - int64_t price; - // howto ensure price when block it confirms it not known - // get price from oracle + current chaintip - // normalize leveraged amount - if ( dir > 0 ) - rektprice = price * leverage / (leverage-1); - else rektprice = price * (leverage-1) / leverage; - return(price); -} - -CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) +CScript prices_betopret(CPubKey mypk,int32_t height,int64_t amount,int16_t leverage,int64_t firstprice,std::vector vec,uint256 tokenid) { CScript opret; - fprintf(stderr,"implement EncodePricesFundingOpRet\n"); + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'B' << mypk << height << amount << leverage << firstprice << vec << tokenid); return(opret); } -uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) +uint8_t prices_betopretdecode(CScript scriptPubKey,CPubKey &pk,int32_t &height,int64_t &amount,int16_t &leverage,int64_t &firstprice,std::vector &vec,uint256 &tokenid) { - fprintf(stderr,"implement DecodePricesFundingOpRet\n"); + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B' ) + { + return(f); + } + return(0); +} + +CScript prices_addopret(uint256 bettxid,CPubKey mypk,int64_t amount) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'A' << bettxid << mypk << amount); + return(opret); +} + +uint8_t prices_addopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int64_t &amount) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> pk; ss >> amount) != 0 && e == EVAL_PRICES && f == 'A' ) + { + return(f); + } + return(0); +} + +CScript prices_costbasisopret(uint256 bettxid,CPubKey mypk,int32_t height,int64_t costbasis) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'C' << bettxid << mypk << height << costbasis); + return(opret); +} + +uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> pk; ss >> height; ss >> costbasis) != 0 && e == EVAL_PRICES && f == 'C' ) + { + return(f); + } + return(0); +} + +CScript prices_finalopret(uint256 bettxid,int64_t profits,int32_t height,CPubKey mypk,int64_t firstprice,int64_t costbasis,int64_t addedbets,int64_t positionsize,int16_t leverage) +{ + CScript opret; + opret << OP_RETURN << E_MARSHAL(ss << EVAL_PRICES << 'F' << bettxid << profits << height << mypk << firstprice << costbasis << addedbets << positionsize << leverage); + return(opret); +} + +uint8_t prices_finalopretdecode(CScript scriptPubKey,uint256 &bettxid,int64_t &profits,int32_t &height,CPubKey &pk,int64_t &firstprice,int64_t &costbasis,int64_t &addedbets,int64_t &positionsize,int16_t &leverage) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> bettxid; ss >> profits; ss >> height; ss >> pk; ss >> firstprice; ss >> costbasis; ss >> addedbets; ss >> positionsize; ss >> leverage) != 0 && e == EVAL_PRICES && f == 'F' ) + { + return(f); + } return(0); } bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; - - return true; // TODO remove, for test dual-evals - - return eval->Invalid("no validation yet"); - std::vector > txids; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Pricesget invalid amount\n"); - return false; - } - //else - { - txid = tx.GetHash(); - memcpy(hash,&txid,sizeof(hash)); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Pricesget validated\n"); - else fprintf(stderr,"Pricesget invalid\n"); - return(retval); - } - } + return true; } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) +int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,int64_t total,int32_t maxinputs,uint256 vintxid,int32_t vinvout) { - // add threshold check int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; SetCCunspents(unspentOutputs,destaddr); @@ -141,11 +232,11 @@ int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char { txid = it->first.txhash; vout = (int32_t)it->first.index; - // need to prevent dup + if ( vout == vinvout && txid == vintxid ) + continue; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) { - // need to verify assetid - if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + if ( (nValue= vintx.vout[vout].nValue) >= total/maxinputs && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); @@ -160,9 +251,461 @@ int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char return(totalinputs); } +UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +int32_t prices_syntheticvec(std::vector &vec,std::vector synthetic) +{ + int32_t i,need,ind,depth = 0; std::string opstr; uint16_t opcode,weight; + if ( synthetic.size() == 0 ) + return(-1); + for (i=0; i= 0 ) + opcode = ind, need = 0; + else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) + { + opcode = PRICES_WEIGHT | weight; + need = 1; + } else return(-2); + if ( depth < need ) + return(-3); + depth -= need; + if ( (opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT ) // weight + depth++; + if ( depth > 3 ) + return(-4); + vec.push_back(opcode); + } + if ( depth != 0 ) + { + fprintf(stderr,"depth.%d not empty\n",depth); + return(-5); + } + return(0); +} + +int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) +{ + int32_t i,ind,errcode,depth,retval = -1; uint16_t opcode; int64_t *pricedata,pricestack[4],price,den,a,b,c; + pricedata = (int64_t *)calloc(sizeof(*pricedata)*3,1 + PRICES_DAYWINDOW*2 + PRICES_SMOOTHWIDTH); + price = den = depth = errcode = 0; + for (i=0; i 0 ) + pricestack[depth] = (pricedata[1] > pricedata[2]) ? pricedata[1] : pricedata[2]; // MAX + else pricestack[depth] = (pricedata[1] < pricedata[2]) ? pricedata[1] : pricedata[2]; // MIN + } + } + if ( pricestack[depth] == 0 ) + errcode = -1; + depth++; + break; + case PRICES_WEIGHT: // multiply by weight and consume top of stack by updating price + if ( depth == 1 ) + { + depth--; + price += pricestack[0] * ind; + den += ind; + } else errcode = -2; + break; + case PRICES_MULT: + if ( depth >= 2 ) + { + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * b) / SATOSHIDEN; + } else errcode = -3; + break; + case PRICES_DIV: + if ( depth >= 2 ) + { + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * SATOSHIDEN) / b; + } else errcode = -4; + break; + case PRICES_INV: + if ( depth >= 1 ) + { + a = pricestack[--depth]; + pricestack[depth++] = (SATOSHIDEN * SATOSHIDEN) / a; + } else errcode = -5; + break; + case PRICES_MDD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (((a * SATOSHIDEN) / b) * SATOSHIDEN) / c; + } else errcode = -6; + break; + case PRICES_MMD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (a * b) / c; + } else errcode = -7; + break; + case PRICES_MMM: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = ((a * b) / SATOSHIDEN) * c; + } else errcode = -8; + break; + case PRICES_DDD: + if ( depth >= 3 ) + { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + pricestack[depth++] = (((((SATOSHIDEN * SATOSHIDEN) / a) * SATOSHIDEN) / b) * SATOSHIDEN) / c; + } else errcode = -9; + break; + default: + errcode = -10; + break; + } + if ( errcode != 0 ) + break; + } + free(pricedata); + if ( den == 0 ) + return(-11); + else if ( depth != 0 ) + return(-12); + else if ( errcode != 0 ) + return(errcode); + return(price / den); +} + +int64_t prices_syntheticprofits(int64_t &costbasis,int32_t firstheight,int32_t height,int16_t leverage,std::vector vec,int64_t positionsize,int64_t addedbets) +{ + int64_t price,profits = 0; int32_t minmax; + minmax = (height > firstheight+PRICES_DAYWINDOW); + if ( (price= prices_syntheticprice(vec,height,minmax,leverage)) < 0 ) + { + fprintf(stderr,"unexpected zero synthetic price at height.%d\n",height); + return(0); + } + if ( minmax != 0 ) + { + if ( leverage > 0 && price > costbasis ) + costbasis = price; + else if ( leverage < 0 && (costbasis == 0 || price < costbasis) ) + costbasis = price; + } + profits = ((price * SATOSHIDEN) / costbasis) - SATOSHIDEN; + profits *= leverage * positionsize; + return(positionsize + addedbets + profits); +} + +void prices_betjson(UniValue &result,int64_t profits,int64_t costbasis,int64_t positionsize,int64_t addedbets,int16_t leverage,int32_t firstheight,int64_t firstprice) +{ + result.push_back(Pair("profits",ValueFromAmount(profits))); + result.push_back(Pair("costbasis",ValueFromAmount(costbasis))); + result.push_back(Pair("positionsize",ValueFromAmount(positionsize))); + result.push_back(Pair("addedbets",ValueFromAmount(addedbets))); + result.push_back(Pair("leverage",(int64_t)leverage)); + result.push_back(Pair("firstheight",(int64_t)firstheight)); + result.push_back(Pair("firstprice",ValueFromAmount(firstprice))); +} + +int64_t prices_costbasis(CTransaction bettx) +{ + int64_t costbasis = 0; + // if vout1 is spent, follow and extract costbasis from opreturn + //uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) + + return(costbasis); +} + +int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid) +{ + int64_t addedbets = 0; + // iterate through batons, adding up vout1 -> addedbets + return(addedbets); +} + +UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; char myaddr[64]; std::string rawtx; + if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","leverage too big")); + return(result); + } + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( prices_syntheticvec(vec,synthetic) < 0 || (firstprice= prices_syntheticprice(vec,nextheight-1,1,leverage)) < 0 || vec.size() == 0 || vec.size() > 4096 ) + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid synthetic")); + return(result); + } + if ( AddNormalinputs(mtx,mypk,amount+4*txfee,64) >= amount+4*txfee ) + { + betamount = (amount * 199) / 200; + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode,(amount-betamount)+2*txfee,pricespk)); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,betamount,pricespk,mypk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_betopret(mypk,nextheight-1,amount,leverage,firstprice,vec,zeroid)); + return(prices_rawtxresult(result,rawtx,0)); + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + return(result); +} + +UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) + { + if ( prices_batontxid(batontxid,bettx,bettxid) >= 0 ) + { + mtx.vin.push_back(CTxIn(batontxid,0,CScript())); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_addopret(bettxid,mypk,amount)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","couldnt find batonttxid")); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","not enough funds")); + return(result); +} + +UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + addedbets = prices_batontxid(batontxid,bettx,bettxid); + mtx.vin.push_back(CTxIn(bettxid,1,CScript())); + for (i=0; ievalcode,bettx.vout[1].nValue-myfee-txfee,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_costbasisopret(bettxid,mypk,firstheight+PRICES_DAYWINDOW-1,costbasis)); + return(prices_rawtxresult(result,rawtx,0)); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); +} + +UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid,batontxid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(ignore,firstheight,rektheight,leverage,vec,positionsize,addedbets)) < 0 ) + { + myfee = (positionsize + addedbets) / 500; + } + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + if ( myfee != 0 ) + { + mtx.vin.push_back(CTxIn(bettxid,2,CScript())); + mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[2].nValue-myfee-txfee,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,rektheight,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","position not rekt")); + return(result); + } + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant decode opret")); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); +} + +UniValue PricesCashout(uint64_t txfee,uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,inputsum,ignore,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = PRICES_TXFEE; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,destaddr,pricespk); + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(ignore,firstheight,nextheight-1,leverage,vec,positionsize,addedbets)) < 0 ) + { + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + result.push_back(Pair("result","error")); + result.push_back(Pair("error","position rekt")); + return(result); + } + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + mtx.vin.push_back(CTxIn(bettxid,2,CScript())); + if ( (inputsum= AddPricesInputs(cp,mtx,destaddr,profits+txfee,64,bettxid,2)) > profits+txfee ) + CCchange = (inputsum - profits); + mtx.vout.push_back(CTxOut(bettx.vout[2].nValue + profits,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if ( CCchange >= txfee ) + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,pricespk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,nextheight-1,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); + return(prices_rawtxresult(result,rawtx,0)); + } + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant decode opret")); + return(result); + } + } + return(result); +} + +UniValue PricesInfo(uint256 bettxid,int32_t refheight) +{ + UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; + if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + { + if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + { + costbasis = prices_costbasis(bettx); + addedbets = prices_batontxid(batontxid,bettx,bettxid); + if ( (profits= prices_syntheticprofits(ignore,firstheight,refheight,leverage,vec,positionsize,addedbets)) < 0 ) + { + result.push_back(Pair("rekt",1)); + result.push_back(Pair("rektfee",(positionsize + addedbets) / 500)); + } else result.push_back(Pair("rekt",0)); + result.push_back(Pair("batontxid",batontxid.GetHex())); + prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); + result.push_back(Pair("height",(int64_t)refheight)); + return(result); + } + } + result.push_back(Pair("result","error")); + result.push_back(Pair("error","cant find bettxid")); + return(result); +} + UniValue PricesList() { - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; cp = CCinit(&C,EVAL_PRICES); pricespk = GetUnspendable(cp,0); SetCCtxids(addressIndex,cp->normaladdr); @@ -171,7 +714,7 @@ UniValue PricesList() txid = it->first.txhash; if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) { result.push_back(uint256_str(str,txid)); } @@ -180,274 +723,4 @@ UniValue PricesList() return(result); } -// longtoken satoshis limits long exposure -// shorttoken satoshis limits short exposure -// both must be in the 1of2 CC address with its total supply -// bettoken -std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; - if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) - { - CCerror = "invalid parameter error"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( (N= (int32_t)pubkeys.size()) || N > 15 ) - { - fprintf(stderr,"too many pubkeys N.%d\n",N); - return(""); - } - for (i=0; i 0 ) - { - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - return(""); -} - -UniValue PricesInfo(uint256 fundingtxid) -{ - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - ERR_RESULT("cant find fundingtxid"); - return(result); - } - if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) - { - result.push_back(Pair("result","success")); - result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); - result.push_back(Pair("bettoken",uint256_str(str,bettoken))); - result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); - sprintf(numstr,"%.3f",(double)margin/1000); - result.push_back(Pair("profitmargin",numstr)); - result.push_back(Pair("maxleverage",maxleverage)); - result.push_back(Pair("mode",(int64_t)mode)); - for (i=0; ipubkeys; - if ( amount < 10000 ) - { - CCerror = "amount must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); - if ( inputs > amount+txfee ) - CCchange = (inputs - amount); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); - // add addr2 - - std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys - return(FinalizeCCTx(0,cp,mtx,mypk,txfee, - EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty, - std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey()))))); - } - else - { - CCerror = "cant find enough bet inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - else - { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; - if ( amount < 0 ) - { - amount = -amount; - dir = -1; - } else dir = 1; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - return(""); - } - if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) - { - if ( leverage > maxleverage || leverage < 1 ) - { - fprintf(stderr,"illegal leverage\n"); - return(""); - } - GetCCaddress1of2(cp,houseaddr,pricespk,planpk); - GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); - if ( dir < 0 ) - tokenid = shorttoken; - else tokenid = longtoken; - exposure = leverage * amount; - longexposure = CCtoken_balance(exposureaddr,longtoken); - shortexposure = CCtoken_balance(exposureaddr,shorttoken); - netexposure = (longexposure - shortexposure + exposure*dir); - if ( netexposure < 0 ) - netexposure = -netexposure; - balance = CCtoken_balance(myaddr,bettoken) / COIN; - if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction - { - fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); - return(""); - } - if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) - { - if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) - { - if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) - { - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); - if ( inputs > exposure+txfee ) - CCchange = (inputs - exposure); - if ( inputs2 > amount+txfee ) - CCchange2 = (inputs2 - amount); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); - // add addr2 and addr3 - //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); - CScript opret; - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - else - { - fprintf(stderr,"cant find enough bettoken inputs\n"); - return(""); - } - } - else - { - fprintf(stderr,"cant find enough exposure inputs\n"); - return(""); - } - } - else - { - CCerror = "cant find enough inputsB"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } - return(""); -} - -UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - UniValue result(UniValue::VOBJ); - // get height of bettxid - // get price and rekt - // get current height and price - // what about if rekt in the past? - return(result); -} - -std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) -{ - return(""); -} - diff --git a/src/clientversion.cpp b/src/clientversion.cpp index 663e2e937..084bbd5ce 100644 --- a/src/clientversion.cpp +++ b/src/clientversion.cpp @@ -35,7 +35,7 @@ * for both bitcoind and bitcoin-core, to make it harder for attackers to * target servers or GUI users specifically. */ -const std::string CLIENT_NAME = GetArg("-ac_clientname", "MagicBean"); +const std::string CLIENT_NAME = GetArg("-clientname", "MagicBean"); /** * Client version number diff --git a/src/init.cpp b/src/init.cpp index d2546347d..24b32a720 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -367,6 +367,7 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-blocknotify=", _("Execute command when the best block changes (%s in cmd is replaced by block hash)")); strUsage += HelpMessageOpt("-checkblocks=", strprintf(_("How many blocks to check at startup (default: %u, 0 = all)"), 288)); strUsage += HelpMessageOpt("-checklevel=", strprintf(_("How thorough the block verification of -checkblocks is (0-4, default: %u)"), 3)); + strUsage += HelpMessageOpt("-clientname=", _("Full node client name, default 'MagicBean'")); strUsage += HelpMessageOpt("-conf=", strprintf(_("Specify configuration file (default: %s)"), "komodo.conf")); if (mode == HMM_BITCOIND) { @@ -571,7 +572,6 @@ std::string HelpMessage(HelpMessageMode mode) strUsage += HelpMessageOpt("-ac_cclib", _("Cryptoconditions dynamicly loadable library")); strUsage += HelpMessageOpt("-ac_ccenable", _("Cryptoconditions to enable")); strUsage += HelpMessageOpt("-ac_ccactivate", _("Block height to enable Cryptoconditions")); - strUsage += HelpMessageOpt("-ac_clientname", _("Full node client name, default 'MagicBean'")); strUsage += HelpMessageOpt("-ac_decay", _("Percentage of block reward decrease at each halving")); strUsage += HelpMessageOpt("-ac_end", _("Block height at which block rewards will end")); strUsage += HelpMessageOpt("-ac_eras", _("Block reward eras")); diff --git a/src/komodo_defs.h b/src/komodo_defs.h index b0f618e93..49aa67081 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -37,6 +37,7 @@ #define KOMODO_MAXNVALUE (((uint64_t)1 << 63) - 1) #define KOMODO_BIT63SET(x) ((x) & ((uint64_t)1 << 63)) #define KOMODO_VALUETOOBIG(x) ((x) > (uint64_t)10000000001*COIN) +#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); @@ -59,6 +60,7 @@ extern uint32_t ASSETCHAINS_VERUSHASH, ASSETCHAINS_VERUSHASHV1_1, ASSETCHAINS_NO extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; extern std::string NOTARY_PUBKEY,ASSETCHAINS_OVERRIDE_PUBKEY,ASSETCHAINS_SCRIPTPUB; extern uint8_t NOTARY_PUBKEY33[33],ASSETCHAINS_OVERRIDE_PUBKEY33[33],ASSETCHAINS_MARMARA; +extern std::vector ASSETCHAINS_PRICES; extern char ASSETCHAINS_SYMBOL[65]; extern int32_t VERUS_BLOCK_POSUNITS, VERUS_CONSECUTIVE_POS_THRESHOLD, VERUS_NOPOS_THRESHHOLD; @@ -82,5 +84,6 @@ int tx_height( const uint256 &hash ); extern char NOTARYADDRS[64][36]; extern uint8_t NUM_NOTARIES; void komodo_netevent(std::vector payload); +int32_t komodo_priceind(char *symbol); #endif diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 3ed1cab49..ba15746e0 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -1551,11 +1551,35 @@ void komodo_passport_iteration() extern std::vector Mineropret; // opreturn data set by the data gathering code #define PRICES_MAXCHANGE (COIN / 100) // maximum acceptable change, set at 1% #define PRICES_SIZEBIT0 (sizeof(uint32_t) * 4) // 4 uint32_t unixtimestamp, BTCUSD, BTCGBP and BTCEUR +#define KOMODO_LOCALPRICE_CACHESIZE 7 +#define KOMODO_MAXPRICES 2048 + +#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"CBCOINBASE",cmdstr,0,0,0) + +const char *Cryptos[] = { "KMD", "ETH" }; // must be on binance (for now) +// "LTC", "BCHABC", "XMR", "IOTA", "ZEC", "WAVES", "LSK", "DCR", "RVN", "DASH", "XEM", "BTS", "ICX", "HOT", "STEEM", "ENJ", "STRAT" +const char *Forex[] = +{ "BGN","NZD","ILS","RUB","CAD","PHP","CHF","AUD","JPY","TRY","HKD","MYR","HRK","CZK","IDR","DKK","NOK","HUF","GBP","MXN","THB","ISK","ZAR","BRL","SGD","PLN","INR","KRW","RON","CNY","SEK","EUR" +}; // must be in ECB list + +uint32_t PriceCache[KOMODO_LOCALPRICE_CACHESIZE][KOMODO_MAXPRICES];//4+sizeof(Cryptos)/sizeof(*Cryptos)+sizeof(Forex)/sizeof(*Forex)]; +int64_t PriceMult[KOMODO_MAXPRICES]; +int32_t komodo_cbopretsize(uint64_t flags); + +void komodo_PriceCache_shift() +{ + int32_t i; + for (i=KOMODO_LOCALPRICE_CACHESIZE-1; i>0; i--) + memcpy(PriceCache[i],PriceCache[i-1],sizeof(PriceCache[i])); + memcpy(PriceCache[0],Mineropret.data(),Mineropret.size()); +} // komodo_heightpricebits() extracts the price data in the coinbase for nHeight -int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) +int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight) { CBlockIndex *pindex; CBlock block; CTransaction tx; int32_t numvouts; std::vector vopret; + if ( seedp != 0 ) + *seedp = 0; if ( (pindex= komodo_chainactive(nHeight)) != 0 ) { if ( komodo_blockload(block,pindex) == 0 ) @@ -1565,8 +1589,10 @@ int32_t komodo_heightpricebits(uint32_t prevbits[4],int32_t nHeight) GetOpReturnData(tx.vout[numvouts-1].scriptPubKey,vopret); if ( vopret.size() >= PRICES_SIZEBIT0 ) { - memcpy(prevbits,vopret.data(),vopret.size()); - return(0); + if ( seedp != 0 ) + memcpy(seedp,&pindex->hashMerkleRoot,sizeof(*seedp)); + memcpy(heightbits,vopret.data(),vopret.size()); + return((int32_t)(vopret.size()/sizeof(uint32_t))); } } } @@ -1590,7 +1616,7 @@ uint32_t komodo_pricenew(char *maxflagp,uint32_t price,uint32_t refprice,int64_t lowprice--; if ( price >= highprice ) { - fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); + //fprintf(stderr,"high %u vs h%llu l%llu tolerance.%llu\n",price,(long long)highprice,(long long)lowprice,(long long)tolerance); if ( price > highprice ) // return non-zero only if we violate the tolerance { *maxflagp = 2; @@ -1629,7 +1655,7 @@ int32_t komodo_pricecmp(int32_t nHeight,int32_t n,char *maxflags,uint32_t *price // komodo_priceclamp() clamps any price that is beyond tolerance int32_t komodo_priceclamp(int32_t n,uint32_t *pricebits,uint32_t *refprices,int64_t tolerance) { - int32_t i; uint32_t newprice; char maxflags[2048]; + int32_t i; uint32_t newprice; char maxflags[KOMODO_MAXPRICES]; memset(maxflags,0,sizeof(maxflags)); for (i=1; i= PRICES_SIZEBIT0 ) { n = (int32_t)(Mineropret.size() / sizeof(uint32_t)); @@ -1655,11 +1681,18 @@ CScript komodo_mineropret(int32_t nHeight) memcpy(pricebits,Mineropret.data(),Mineropret.size()); for (i=numzero=0; i 0 ) { memcpy(pricebits,Mineropret.data(),Mineropret.size()); memset(maxflags,0,sizeof(maxflags)); @@ -1667,7 +1700,7 @@ CScript komodo_mineropret(int32_t nHeight) { // if the new prices are outside tolerance, update Mineropret with clamped prices komodo_priceclamp(n,pricebits,prevbits,PRICES_MAXCHANGE); - fprintf(stderr,"update Mineropret to clamped prices\n"); + //fprintf(stderr,"update Mineropret to clamped prices\n"); memcpy(Mineropret.data(),pricebits,Mineropret.size()); } } @@ -1687,18 +1720,16 @@ CScript komodo_mineropret(int32_t nHeight) The only way komodo_opretvalidate() doesnt return an error is if maxflag is set or it is within tolerance of both the prior block and the local data. The local data validation only happens if it is a recent block and not a block from the past as the local node is only getting the current price data. */ -// reconsiderblock 002aca768b09dfcf36bd934ab34b23983148b116e12cb0b6e1a3f895d1db63aa +// for PRICES: reconsiderblock 002aca768b09dfcf36bd934ab34b23983148b116e12cb0b6e1a3f895d1db63aa // and // reconsiderblock 0034cf582018eacc0b4ae001491ce460113514cb1a3f217567ef4a2207de361a // reconsiderbloc 000abf51c023b64af327c50c1b060797b8cb281c696d30ab92fd002a8b8c9aea // are needed to sync past initial blocks with different data set -// pass in blockhash and nTime, latch if it is rejected due to local price, then if localprice changes in a way that would validate then issue reconsiderblock -// add rpc call for extracting rawprices int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,int32_t nHeight,CScript scriptPubKey) { - int32_t testchain_exemption = 350; - std::vector vopret; char maxflags[2048]; double btcusd,btcgbp,btceur; uint32_t localbits[2048],pricebits[2048],prevbits[2048],newprice; int32_t i,prevtime,maxflag,lag,lag2,lag3,n; uint32_t now = (uint32_t)time(NULL); + int32_t testchain_exemption = 0; + std::vector vopret; char maxflags[KOMODO_MAXPRICES]; double btcusd,btcgbp,btceur; uint32_t localbits[KOMODO_MAXPRICES],pricebits[KOMODO_MAXPRICES],prevbits[KOMODO_MAXPRICES],newprice; int32_t i,j,prevtime,maxflag,lag,lag2,lag3,n,errflag,iter; uint32_t now = (uint32_t)time(NULL); if ( ASSETCHAINS_CBOPRET != 0 && nHeight > 0 ) { GetOpReturnData(scriptPubKey,vopret); @@ -1718,7 +1749,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i fprintf(stderr,"A ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3); return(-1); } - if ( lag2 < -ASSETCHAINS_BLOCKTIME ) // must be close to last block timestamp + if ( lag2 < -60 ) //testchain_exemption ) // must be close to last block timestamp { fprintf(stderr,"B ht.%d now.%u htstamp.%u %u - pricebits[0] %u -> lags.%d %d %d vs %d cmp.%d\n",nHeight,now,prevtime,block->nTime,pricebits[0],lag,lag2,lag3,ASSETCHAINS_BLOCKTIME,lag2<-ASSETCHAINS_BLOCKTIME); if ( nHeight > testchain_exemption ) @@ -1734,7 +1765,7 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i btcgbp = (double)pricebits[2]/10000; btceur = (double)pricebits[3]/10000; fprintf(stderr,"ht.%d: lag.%d %.4f USD, %.4f GBP, %.4f EUR, GBPUSD %.6f, EURUSD %.6f, EURGBP %.6f [%d]\n",nHeight,lag,btcusd,btcgbp,btceur,btcusd/btcgbp,btcusd/btceur,btcgbp/btceur,lag2); - if ( komodo_heightpricebits(prevbits,nHeight-1) == 0 ) + if ( komodo_heightpricebits(0,prevbits,nHeight-1) > 0 ) { if ( nHeight < testchain_exemption ) { @@ -1764,16 +1795,51 @@ int32_t komodo_opretvalidate(const CBlock *block,CBlockIndex * const previndex,i if ( localbits[i] == 0 ) localbits[i] = prevbits[i]; } - for (i=1; i 0 && localbits[i] < prevbits[i] ) - return(-1); - else if ( maxflag < 0 && localbits[i] > prevbits[i] ) - return(-1); + if ( (maxflag= maxflags[i]) != 0 && localbits[i] != 0 ) + { + // make sure local price is moving in right direction + fprintf(stderr,"maxflag.%d i.%d localbits.%u vs pricebits.%u prevbits.%u\n",maxflag,i,localbits[i],pricebits[i],prevbits[i]); + if ( maxflag > 0 && localbits[i] < prevbits[i] ) + { + if ( iter == 0 ) + break; + // second iteration checks recent prices to see if within local volatility + for (j=0; j= prevbits[i] ) + { + fprintf(stderr,"i.%d within recent localprices[%d] %u >= %u\n",i,j,PriceCache[j][i],prevbits[i]); + break; + } + if ( j == KOMODO_LOCALPRICE_CACHESIZE ) + break; + } + else if ( maxflag < 0 && localbits[i] > prevbits[i] ) + { + if ( iter == 0 ) + break; + for (j=0; j strvec) { - int32_t i,errs=0; uint32_t price; - for (i=0; i 333 ) - ASSETCHAINS_CBOPRET = 7; size = PRICES_SIZEBIT0; if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) - size += sizeof(forexprices); + size += (sizeof(Forex)/sizeof(*Forex)) * sizeof(uint32_t); if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) - size += sizeof(cryptoprices); + size += (sizeof(Cryptos)/sizeof(*Cryptos) + ASSETCHAINS_PRICES.size())*sizeof(uint32_t); + } + return(size); +} + +void komodo_cbopretupdate(int32_t forceflag) +{ + static uint32_t lasttime,lastcrypto,lastbtc,pending; + static uint32_t pricebits[4],cryptoprices[KOMODO_MAXPRICES],forexprices[sizeof(Forex)/sizeof(*Forex)]; + int32_t size; uint32_t flags=0,now; + if ( forceflag != 0 && pending != 0 ) + { + while ( pending != 0 ) + fprintf(stderr,"pricewait "), sleep(1); + return; + } + pending = 1; + now = (uint32_t)time(NULL); + if ( (ASSETCHAINS_CBOPRET & 1) != 0 ) + { +//if ( komodo_nextheight() > 333 ) // for debug only! +// ASSETCHAINS_CBOPRET = 7; + size = komodo_cbopretsize(ASSETCHAINS_CBOPRET); if ( Mineropret.size() < size ) Mineropret.resize(size); size = PRICES_SIZEBIT0; - if ( now > lastbtc+120 && get_btcusd(pricebits) == 0 ) + if ( (forceflag != 0 || now > lastbtc+120) && get_btcusd(pricebits) == 0 ) { - memcpy(Mineropret.data(),pricebits,PRICES_SIZEBIT0); - lastbtc = (uint32_t)time(NULL); + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(PriceCache[0],pricebits,PRICES_SIZEBIT0); + flags |= 1; } if ( (ASSETCHAINS_CBOPRET & 2) != 0 ) { - if ( now > lasttime+3600*5 || forexprices[0] == 0 ) + if ( now > lasttime+3600*5 || forexprices[0] == 0 ) // cant assume timestamp is valid for forex price as it is a daily weekday changing thing anyway. { get_dailyfx(forexprices); - memcpy(&Mineropret.data()[size],forexprices,sizeof(forexprices)); - lasttime = (uint32_t)time(NULL); + if ( flags == 0 ) + komodo_PriceCache_shift(); + flags |= 2; + memcpy(&PriceCache[0][size/sizeof(uint32_t)],forexprices,sizeof(forexprices)); } - size += sizeof(forexprices); + size += (sizeof(Forex)/sizeof(*Forex)) * sizeof(uint32_t); } if ( (ASSETCHAINS_CBOPRET & 4) != 0 ) { - if ( now > lastcrypto+100 ) + if ( forceflag != 0 || flags != 0 ) { - get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos))); - memcpy(&Mineropret.data()[size],cryptoprices,sizeof(cryptoprices)); - lastcrypto = (uint32_t)time(NULL); + get_cryptoprices(cryptoprices,Cryptos,(int32_t)(sizeof(Cryptos)/sizeof(*Cryptos)),ASSETCHAINS_PRICES); + if ( flags == 0 ) + komodo_PriceCache_shift(); + memcpy(&PriceCache[0][size/sizeof(uint32_t)],cryptoprices,(sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t)); + flags |= 4; // very rarely we can see flags == 6 case } - size += sizeof(cryptoprices); + size += (sizeof(Cryptos)/sizeof(*Cryptos)+ASSETCHAINS_PRICES.size()) * sizeof(uint32_t); + } + if ( flags != 0 ) + { + now = (uint32_t)time(NULL); + if ( (flags & 1) != 0 ) + lastbtc = now; + if ( (flags & 2) != 0 ) + lasttime = now; + if ( (flags & 4) != 0 ) + lastcrypto = now; + memcpy(Mineropret.data(),PriceCache[0],size); + // high volatility still strands nodes so we need to check new prices to approve a stuck block + // scan list of stuck blocks (one?) and auto reconsiderblock if it changed state + + //int32_t i; for (i=0; i= KOMODO_MAXPRICES ) + return(-1); + mult = PriceMult[ind]; + if ( nonzprices != 0 ) + memset(nonzprices,0,sizeof(*nonzprices)*PRICES_DAYWINDOW); + for (iter=0; iter= PRICES_DAYWINDOW ) + i = 0; + if ( (price= rawprices[i*rawskip]) == 0 ) + { + fprintf(stderr,"null rawprice.[%d]\n",i); + return(-1); + } + if ( price >= lowprice && price <= highprice ) + { + //fprintf(stderr,"%.1f ",(double)price/10000); + sum += price; + correlation++; + if ( correlation > (PRICES_DAYWINDOW>>1) ) + { + if ( nonzprices == 0 ) + return(refprice * mult); + //fprintf(stderr,"-> %.4f\n",(double)sum*mult/correlation); + //return(sum*mult/correlation); + n = 0; + i = (iter + seed) % PRICES_DAYWINDOW; + for (k=0; k= PRICES_DAYWINDOW ) + i = 0; + if ( n > (PRICES_DAYWINDOW>>1) ) + nonzprices[i] = 0; + else + { + price = rawprices[i*rawskip]; + if ( price < lowprice || price > highprice ) + nonzprices[i] = 0; + else + { + nonzprices[i] = price; + //fprintf(stderr,"(%d %u) ",i,rawprices[i*rawskip]); + n++; + } + } + } + //fprintf(stderr,"ind.%d iter.%d j.%d i.%d n.%d correlation.%d ref %llu -> %llu\n",ind,iter,j,i,n,correlation,(long long)refprice,(long long)sum/correlation); + if ( n != correlation ) + return(-1); + sum = den = n = 0; + for (i=0; i %.8f\n",(long long)firstprice,((double)(sum*mult) / den) / COIN); + return((sum * mult) / den); + } + } + } + if ( correlation > maxcorrelation ) + maxcorrelation = correlation; + } + fprintf(stderr,"ind.%d iter.%d maxcorrelation.%d ref.%llu high.%llu low.%llu\n",ind,iter,maxcorrelation,(long long)refprice,(long long)highprice,(long long)lowprice); + return(0); +} + +int64_t _pairave64(int64_t valA,int64_t valB) +{ + if ( valA != 0 && valB != 0 ) + return((valA + valB) / 2); + else if ( valA != 0 ) return(valA); + else return(valB); +} + +int64_t _pairdiff64(register int64_t valA,register int64_t valB) +{ + if ( valA != 0 && valB != 0 ) + return(valA - valB); + else return(0); +} + +int64_t balanced_ave64(int64_t buf[],int32_t i,int32_t width) +{ + register int32_t nonz,j; register int64_t sum,price; + nonz = 0; + sum = 0; + for (j=-width; j<=width; j++) + { + price = buf[i + j]; + if ( price != 0 ) + { + sum += price; + nonz++; + } + } + if ( nonz != 0 ) + sum /= nonz; + return(sum); +} + +void buf_trioave64(int64_t dest[],int64_t src[],int32_t n) +{ + register int32_t i,j,width = 3; + for (i=0; i<128; i++) + src[i] = 0; + //for (i=n-width-1; i>width; i--) + // dest[i] = balanced_ave(src,i,width); + //for (i=width; i>0; i--) + // dest[i] = balanced_ave(src,i,i); + for (i=1; i ASSETCHAINS_PRICES; #define _ASSETCHAINS_EQUIHASH 0 uint32_t ASSETCHAINS_NUMALGOS = 3; diff --git a/src/komodo_utils.h b/src/komodo_utils.h index a91dae1e8..2d0fe4f4e 100644 --- a/src/komodo_utils.h +++ b/src/komodo_utils.h @@ -1664,12 +1664,13 @@ uint64_t komodo_ac_block_subsidy(int nHeight) } extern int64_t MAX_MONEY; -void komodo_cbopretupdate(); +void komodo_cbopretupdate(int32_t forceflag); +void SplitStr(const std::string& strVal, std::vector &outVals); void komodo_args(char *argv0) { extern const char *Notaries_elected1[][2]; - std::string name,addn,hexstr; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; + std::string name,addn,hexstr,symbol; char *dirname,fname[512],arg0str[64],magicstr[9]; uint8_t magic[4],extrabuf[8192],disablebits[32],*extraptr=0; FILE *fp; uint64_t val; uint16_t port; int32_t i,nonz=0,baseid,len,n,extralen = 0; uint64_t ccenables[256]; IS_KOMODO_NOTARY = GetBoolArg("-notary", false); IS_STAKED_NOTARY = GetArg("-stakednotary", -1); if ( IS_STAKED_NOTARY != -1 && IS_KOMODO_NOTARY == true ) { @@ -1734,7 +1735,7 @@ void komodo_args(char *argv0) { printf("KOMODO_REWIND %d\n",KOMODO_REWIND); } - if ( name.c_str()[0] != 0 ) + if ( name.c_str()[0] != 0 ) { std::string selectedAlgo = GetArg("-ac_algo", std::string(ASSETCHAINS_ALGORITHMS[0])); @@ -1812,6 +1813,13 @@ void komodo_args(char *argv0) ASSETCHAINS_CODAPORT = GetArg("-ac_coda",0); ASSETCHAINS_MARMARA = GetArg("-ac_marmara",0); ASSETCHAINS_CBOPRET = GetArg("-ac_cbopret",0); + if ( ASSETCHAINS_CBOPRET != 0 ) + { + SplitStr(GetArg("-ac_prices",""), ASSETCHAINS_PRICES); + for (i=0; i sizeof(heights)/sizeof(*heights) ) - maxsamples = sizeof(heights)/sizeof(*heights); - ret.push_back(Pair("base", base)); - ret.push_back(Pair("rel", rel)); - n = komodo_paxprices(heights,prices,maxsamples,(char *)base.c_str(),(char *)rel.c_str()); + nextheight = komodo_nextheight(); UniValue a(UniValue::VARR); - for (i=0; i2; i++,ht--) { - UniValue item(UniValue::VOBJ); - if ( heights[i] < 0 || heights[i] > chainActive.Height() ) + if ( ht < 0 || ht > chainActive.Height() ) throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range"); else { - CBlockIndex *pblockindex = chainActive[heights[i]]; - - item.push_back(Pair("t", (int64_t)pblockindex->nTime)); - item.push_back(Pair("p", (double)prices[i] / COIN)); - a.push_back(item); + if ( (n= komodo_heightpricebits(0,rawprices,ht)) > 0 ) + { + if ( n != numpricefeeds ) + throw JSONRPCError(RPC_INVALID_PARAMETER, "numprices != first numprices"); + else + { + for (j=0; j= width ) + { + for (i=0; i ignoredMap = { \ + {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, \ + {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, \ + {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, \ + {"RBM5LofZFodMeewUzoMWcxedm3L3hYRaWg", 1}, \ + {"RAdcko2d94TQUcJhtFHZZjMyWBKEVfgn4J", 1}, \ + {"RLzUaZ934k2EFCsAiVjrJqM8uU1vmMRFzk", 1}, \ + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, \ + {"RUDrX1v5toCsJMUgtvBmScKjwCB5NaR8py", 1}, \ + {"RMSZMWZXv4FhUgWhEo4R3AQXmRDJ6rsGyt", 1}, \ + {"RRvwmbkxR5YRzPGL5kMFHMe1AH33MeD8rN", 1}, \ + {"RQLQvSgpPAJNPgnpc8MrYsbBhep95nCS8L", 1}, \ + {"RK8JtBV78HdvEPvtV5ckeMPSTojZPzHUTe", 1}, \ + {"RHVs2KaCTGUMNv3cyWiG1jkEvZjigbCnD2", 1}, \ + {"RE3SVaDgdjkRPYA6TRobbthsfCmxQedVgF", 1}, \ + {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, \ + {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, \ + {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} \ +}; + +int32_t CBlockTreeDB::Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr) +{ + int64_t total = 0; int64_t totalAddresses = 0; std::string address; + int64_t utxos = 0; int64_t ignoredAddresses = 0; + DECLARE_IGNORELIST + boost::scoped_ptr iter(NewIterator()); + std::map addressAmounts; + for (iter->SeekToLast(); iter->Valid(); iter->Prev()) + { + boost::this_thread::interruption_point(); + try + { + std::vector slKey = std::vector(); + pair keyObj; + iter->GetKey(keyObj); + char chType = keyObj.first; + CAddressIndexIteratorKey indexKey = keyObj.second; + //fprintf(stderr, "chType=%d\n", chType); + if (chType == DB_ADDRESSUNSPENTINDEX) + { + try { + CAmount nValue; + iter->GetValue(nValue); + getAddressFromIndex(indexKey.type, indexKey.hashBytes, address); + if ( nValue > dustthreshold ) + { + std::map ::iterator ignored = ignoredMap.find(address); + if (ignored != ignoredMap.end()) + { + fprintf(stderr,"ignoring %s\n", address.c_str()); + ignoredAddresses++; + continue; + } + std::map ::iterator pos = addressAmounts.find(address); + if ( pos == addressAmounts.end() ) + { + // insert new address + utxo amount + //fprintf(stderr, "inserting new address %s with amount %li\n", address.c_str(), nValue); + addressAmounts[address] = nValue; + totalAddresses++; + } + else + { + // update unspent tally for this address + //fprintf(stderr, "updating address %s with new utxo amount %li\n", address.c_str(), nValue); + addressAmounts[address] += nValue; + } + //fprintf(stderr,"{\"%s\", %.8f},\n",address.c_str(),(double)nValue/COIN); + // total += nValue; + utxos++; + } else fprintf(stderr,"ignoring amount=0 UTXO for %s\n", address.c_str()); + } + catch (const std::exception& e) + { + fprintf(stderr, "DONE %s: LevelDB addressindex exception! - %s\n", __func__, e.what()); + break; + } + } + } + catch (const std::exception& e) + { + fprintf(stderr, "DONE reading index entries\n"); + break; + } + } + 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) + { + //obj.push_back( make_pair("addr", it->second.c_str() ) ); + //char amount[32]; + //sprintf(amount, "%.8f", (double) it->first / COIN); + //obj.push_back( make_pair("amount", amount) ); + //obj.push_back( make_pair("segid",(int32_t)komodo_segid32((char *)it->second.c_str()) & 0x3f) ); + //addressesSorted.push_back(obj); + total += it->first; + topN++; + // If requested, only show top N addresses in output JSON + if ( top == topN ) + break; + } + return(topN); +} + UniValue CBlockTreeDB::Snapshot(int top) { int64_t total = 0; int64_t totalAddresses = 0; std::string address; int64_t utxos = 0; int64_t ignoredAddresses = 0; + DECLARE_IGNORELIST boost::scoped_ptr iter(NewIterator()); std::map addressAmounts; std::vector > vaddr; UniValue result(UniValue::VOBJ); result.push_back(Pair("start_time", (int) time(NULL))); - std::map ignoredMap = { + /* std::map ignoredMap = { {"RReUxSs5hGE39ELU23DfydX8riUuzdrHAE", 1}, {"RMUF3UDmzWFLSKV82iFbMaqzJpUnrWjcT4", 1}, {"RA5imhVyJa7yHhggmBytWuDr923j2P1bxx", 1}, @@ -466,7 +574,7 @@ UniValue CBlockTreeDB::Snapshot(int top) {"RW6S5Lw5ZCCvDyq4QV9vVy7jDHfnynr5mn", 1}, {"RTkJwAYtdXXhVsS3JXBAJPnKaBfMDEswF8", 1}, {"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY", 1} //Burnaddress for null privkey - }; + };*/ int64_t startingHeight = chainActive.Height(); //fprintf(stderr, "Starting snapshot at height %lli\n", startingHeight); diff --git a/src/txdb.h b/src/txdb.h index b9bae2fe4..9b80b922b 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -116,6 +116,7 @@ public: bool LoadBlockIndexGuts(); bool blockOnchainActive(const uint256 &hash); UniValue Snapshot(int top); + int32_t Snapshot2(int64_t dustthreshold,int32_t top,std::vector > &vaddr); }; #endif // BITCOIN_TXDB_H diff --git a/src/util.cpp b/src/util.cpp index 2abbf5bef..980d82ac9 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -394,6 +394,30 @@ void ParseParameters(int argc, const char* const argv[]) } } +void SplitStr(const std::string& strVal, std::vector &outVals) +{ + stringstream ss(strVal); + std::string str; + + while ( ss.peek() == ' ' ) + ss.ignore(); + + while ( ss >> str ) + { + if ( str.size() == 0 ) + continue; + if ( str[str.size()-1] == ',' ) + str.resize(str.size()-1); + outVals.push_back(str); + while ( ss.peek() == ' ' ) + ss.ignore(); + if ( ss.peek() == ',' ) + ss.ignore(); + while ( ss.peek() == ' ' ) + ss.ignore(); + } +} + void Split(const std::string& strVal, uint64_t *outVals, const uint64_t nDefault) { stringstream ss(strVal); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 107508c18..d720cf33f 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -6105,6 +6105,7 @@ UniValue channelsopen(const UniValue& params, bool fHelp) tokenid=Parseuint256((char *)params[3].get_str().c_str()); } hex = ChannelOpen(0,pubkey2pk(destpub),numpayments,payment,tokenid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6135,6 +6136,7 @@ UniValue channelspayment(const UniValue& params, bool fHelp) secret = Parseuint256((char *)params[2].get_str().c_str()); } hex = ChannelPayment(0,opentxid,amount,secret); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6155,6 +6157,7 @@ UniValue channelsclose(const UniValue& params, bool fHelp) LOCK2(cs_main, pwalletMain->cs_wallet); opentxid = Parseuint256((char *)params[0].get_str().c_str()); hex = ChannelClose(0,opentxid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6176,6 +6179,7 @@ UniValue channelsrefund(const UniValue& params, bool fHelp) opentxid = Parseuint256((char *)params[0].get_str().c_str()); closetxid = Parseuint256((char *)params[1].get_str().c_str()); hex = ChannelRefund(0,opentxid,closetxid); + RETURN_IF_ERROR(CCerror); if ( hex.size() > 0 ) { result.push_back(Pair("result", "success")); @@ -6909,143 +6913,14 @@ UniValue priceslist(const UniValue& params, bool fHelp) UniValue pricesinfo(const UniValue& params, bool fHelp) { - uint256 fundingtxid; - if ( fHelp || params.size() != 1 ) + uint256 bettxid; int32_t height; + if ( fHelp || params.size() != 2 ) throw runtime_error("pricesinfo fundingtxid\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - return(PricesInfo(fundingtxid)); -} - -UniValue pricescreate(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); uint64_t mode; int64_t funding; int32_t i,n,margin,maxleverage; std::string hex; uint256 oracletxid,longtoken,shorttoken,bettoken; std::vector pubkeys; std::vectorpubkey; - if ( fHelp || params.size() < 8 ) - throw runtime_error("pricescreate bettoken oracletxid margin mode longtoken shorttoken maxleverage funding N [pubkeys]\n"); - if ( ensure_CCrequirements(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"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - bettoken = Parseuint256((char *)params[0].get_str().c_str()); - oracletxid = Parseuint256((char *)params[1].get_str().c_str()); - margin = atof(params[2].get_str().c_str()) * 1000; - mode = atol(params[3].get_str().c_str()); - longtoken = Parseuint256((char *)params[4].get_str().c_str()); - shorttoken = Parseuint256((char *)params[5].get_str().c_str()); - maxleverage = atol(params[6].get_str().c_str()); - funding = atof(params[7].get_str().c_str()) * COIN + 0.00000000499999; - n = atoi(params[8].get_str().c_str()); - if ( n > 0 ) - { - for (i=0; i 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create prices funding transaction"); - } - return(result); -} - -UniValue pricesaddfunding(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesaddfunding fundingtxid bettoken amount\n"); - if ( ensure_CCrequirements(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"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - hex = PricesAddFunding(0,bettoken,fundingtxid,amount); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesaddfunding transaction"); - } - return(result); -} - -UniValue pricesbet(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid,bettoken; int64_t amount; int32_t leverage; - if ( fHelp || params.size() != 4 ) - throw runtime_error("pricesbet fundingtxid bettoken amount leverage\n"); - if ( ensure_CCrequirements(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"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - leverage = atoi(params[3].get_str().c_str()); - hex = PricesBet(0,bettoken,fundingtxid,amount,leverage); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesbet transaction"); - } - return(result); -} - -UniValue pricesstatus(const UniValue& params, bool fHelp) -{ - uint256 fundingtxid,bettxid,bettoken; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesstatus fundingtxid bettoken bettxid\n"); - if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - return(PricesStatus(0,bettoken,fundingtxid,bettxid)); -} - -UniValue pricesfinish(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); uint256 fundingtxid,bettxid,bettoken; std::string hex; - if ( fHelp || params.size() != 3 ) - throw runtime_error("pricesfinish fundingtxid bettoken bettxid\n"); - if ( ensure_CCrequirements(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"); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - bettoken = Parseuint256((char *)params[1].get_str().c_str()); - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - hex = PricesFinish(0,bettoken,fundingtxid,bettxid); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - else - { - ERR_RESULT("couldnt create pricesfinish transaction"); - } - return(result); + bettxid = Parseuint256((char *)params[0].get_str().c_str()); + height = atoi(params[1].get_str().c_str()); + return(PricesInfo(bettxid,height)); } UniValue dicefund(const UniValue& params, bool fHelp)