From 657bb1e25e34400a382fb69549d73b9c220722c5 Mon Sep 17 00:00:00 2001 From: Duke Date: Thu, 11 Jan 2024 22:30:50 -0500 Subject: [PATCH] remove dice, channels, lotto --- src/Makefile.am | 3 - src/cc/CCchannels.h | 35 - src/cc/CCcustom.cpp | 15 +- src/cc/CCdice.h | 38 - src/cc/CClotto.h | 34 - src/cc/channels.cpp | 807 ---------------- src/cc/dice.cpp | 1871 -------------------------------------- src/cc/lotto.cpp | 344 ------- src/rpc/server.cpp | 22 - src/rpc/server.h | 9 - src/wallet/rpcwallet.cpp | 352 ------- 11 files changed, 6 insertions(+), 3524 deletions(-) delete mode 100644 src/cc/CCchannels.h delete mode 100644 src/cc/CCdice.h delete mode 100644 src/cc/CClotto.h delete mode 100644 src/cc/channels.cpp delete mode 100644 src/cc/dice.cpp delete mode 100644 src/cc/lotto.cpp diff --git a/src/Makefile.am b/src/Makefile.am index f99589472..d376665a5 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -282,13 +282,10 @@ libbitcoin_server_a_SOURCES = \ cc/assets.cpp \ cc/faucet.cpp \ cc/rewards.cpp \ - cc/dice.cpp \ - cc/lotto.cpp \ cc/fsm.cpp \ cc/heir.cpp \ cc/oracles.cpp \ cc/payments.cpp \ - cc/channels.cpp \ cc/auction.cpp \ cc/betprotocol.cpp \ chain.cpp \ diff --git a/src/cc/CCchannels.h b/src/cc/CCchannels.h deleted file mode 100644 index e1fca87e8..000000000 --- a/src/cc/CCchannels.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) 2016-2023 The Hush developers -// Distributed under the GPLv3 software license, see the accompanying -// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - - -#ifndef CC_CHANNELS_H -#define CC_CHANNELS_H - -#include "CCinclude.h" -#define CHANNELS_MAXPAYMENTS 1000 - -bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); -UniValue ChannelOpen(const CPubKey& pk,uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 tokenid); -UniValue ChannelPayment(const CPubKey& pk,uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret); -UniValue ChannelClose(const CPubKey& pk,uint64_t txfee,uint256 opentxid); -UniValue ChannelRefund(const CPubKey& pk,uint64_t txfee,uint256 opentxid,uint256 closetxid); -UniValue ChannelsList(const CPubKey& pk); -// CCcustom -UniValue ChannelsInfo(const CPubKey& pk,uint256 opentxid); - -#endif diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index f23d9e6ae..be3dccd17 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -21,12 +21,9 @@ #include "CCassets.h" #include "CCfaucet.h" #include "CCrewards.h" -#include "CCdice.h" #include "CCauction.h" -#include "CClotto.h" #include "CCfsm.h" #include "CCHeir.h" -#include "CCchannels.h" #include "CCOracles.h" #include "CCPrices.h" #include "CCPegs.h" @@ -325,16 +322,16 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) strcpy(cp->normaladdr,DiceNormaladdr); strcpy(cp->CChexstr,DiceCChexstr); memcpy(cp->CCpriv,DiceCCpriv,32); - cp->validate = DiceValidate; - cp->ismyvin = IsDiceInput; + //cp->validate = DiceValidate; + //cp->ismyvin = IsDiceInput; break; case EVAL_LOTTO: strcpy(cp->unspendableCCaddr,LottoCCaddr); strcpy(cp->normaladdr,LottoNormaladdr); strcpy(cp->CChexstr,LottoCChexstr); memcpy(cp->CCpriv,LottoCCpriv,32); - cp->validate = LottoValidate; - cp->ismyvin = IsLottoInput; + //cp->validate = LottoValidate; + //cp->ismyvin = IsLottoInput; break; case EVAL_FSM: strcpy(cp->unspendableCCaddr,FSMCCaddr); @@ -365,8 +362,8 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) strcpy(cp->normaladdr,ChannelsNormaladdr); strcpy(cp->CChexstr,ChannelsCChexstr); memcpy(cp->CCpriv,ChannelsCCpriv,32); - cp->validate = ChannelsValidate; - cp->ismyvin = IsChannelsInput; + //cp->validate = ChannelsValidate; + //cp->ismyvin = IsChannelsInput; break; case EVAL_ORACLES: strcpy(cp->unspendableCCaddr,OraclesCCaddr); diff --git a/src/cc/CCdice.h b/src/cc/CCdice.h deleted file mode 100644 index b1401ad2a..000000000 --- a/src/cc/CCdice.h +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) 2016-2023 The Hush developers -// Distributed under the GPLv3 software license, see the accompanying -// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - - -#ifndef CC_DICE_H -#define CC_DICE_H - -#include "CCinclude.h" - -#define EVAL_DICE 0xe6 - -bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); - -std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds); -std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyvout,int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout,uint256 vin0txid,int32_t vin0vout); -double DiceStatus(uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid); -std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks); -std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount); -UniValue DiceInfo(uint256 diceid); -UniValue DiceList(); -int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid, int32_t &entropytxs,bool random); - -#endif diff --git a/src/cc/CClotto.h b/src/cc/CClotto.h deleted file mode 100644 index ad9c7131a..000000000 --- a/src/cc/CClotto.h +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2016-2023 The Hush developers -// Distributed under the GPLv3 software license, see the accompanying -// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - - -#ifndef CC_LOTTO_H -#define CC_LOTTO_H - -#include "CCinclude.h" - -#define EVAL_LOTTO 0xe9 - -bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); - -UniValue LottoInfo(uint256 lottoid); -UniValue LottoList(); -std::string LottoTicket(uint64_t txfee,int64_t numtickets); -std::string LottoWinner(uint64_t txfee); - -#endif diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp deleted file mode 100644 index 498047196..000000000 --- a/src/cc/channels.cpp +++ /dev/null @@ -1,807 +0,0 @@ -// Copyright (c) 2016-2023 The Hush developers -// Distributed under the GPLv3 software license, see the accompanying -// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "CCchannels.h" - -/* - The idea here is to allow instant (mempool) payments that are secured by dPoW. In order to simplify things, channels CC will require creating reserves for each payee locked in the destination user's CC address. This will look like the payment is already made, but it is locked until further released. The dPoW protection comes from the cancel channel having a delayed effect until the next notarization. This way, if a payment release is made and the chain reorged, the same payment release will still be valid when it is re-broadcast into the mempool. - - In order to achieve this effect, the payment release needs to be in a special form where its input cannot be spent only by the sender. - - Given sender's payment to dest CC address, only the destination is able to spend, so we need to constrain that spending with a release mechanism. One idea is a 2of2 multisig, but that has the issue of needing confirmation and since a sender utxo is involved, subject to doublespend and we lose the speed. Another idea is release on secrets! since once revealed, the secret remains valid, this method is immune from double spend. Also, there is no worry about an MITM attack as the funds are only spendable by the destination pubkey and only with the secret. The secrets can be sent via any means, including OP_RETURN of normal transaction in the mempool. - - Now the only remaining issue for sending is how to allocate funds to the secrets. This needs to be sent as hashes of secrets when the channel is created. A bruteforce method would be one secret per COIN, but for large amount channels this is cumbersome. A more practical approach is to have a set of secrets for each order of magnitude: - - 123.45 channel funds -> 1x secret100, 2x secret10, 3x secret1, 4x secret.1, 5x secret.01 - 15 secrets achieves the 123.45 channel funding. - - In order to avoid networking issues, the convention can be to send tx to normal address of destination with just an OP_RETURN, for the cost of txfee. For micropayments, a separate method of secret release needs to be established, but that is beyond the scope of this CC. - - There is now the dPoW security that needs to be ensured. In order to close the channel, a tx needs to be confirmed that cancels the channel. As soon as this tx is seen, the destination will know that the channel will be closing soon, if the node is online. If not, the payments cant be credited anyway, so it seems no harm. Even after the channel is closed, it is possible for secrets to be releasing funds, but depending on when the notarization happens, it could invalidate the spends, so it is safest that as soon as the channel cancel tx is confirmed to invalidate any further payments released. - - Given a channelclose and notarization confirmation (or enough blocks), the remaining funds needs to be able to come back to the sender. this means the funds need to be in a 1of2 CC multisig to allow either party to spend. Cheating is prevented by the additional rules of spending, ie. denomination secrets, or channelclose. - - For efficiency we want to allow batch spend with multiple secrets to claim a single total - - Second iteration: - As implementing it, some efficieny gains to be made with a slightly different approach. - Instead of separate secrets for each amount, a hashchain will be used, each releasing the same amount - - To spend, the prior value in the hash chain is published, or can publish N deep. validation takes N hashes. - - Also, in order to be able to track open channels, a tag is needed to be sent and better to send to a normal CC address for a pubkey to isolate the transactions for channel opens. - -Possible third iteration: - Let us try to setup a single "hot wallet" address to have all the channel funds and use it for payments to any destination. If there are no problems with reorgs and double spends, this would allow everyone to be "connected" to everyone else via the single special address. - - So funds -> user's CC address along with hashchain, but likely best to have several utxo to span order of magnitudes. - - a micropayment would then spend a utxo and attach a shared secret encoded unhashed link from the hashchain. That makes the receiver the only one that can decode the actual hashchain's prior value. - - however, since this spend is only spendable by the sender, it is subject to a double spend attack. It seems it is a dead end. Alternative is to use the global CC address, but that commingles all funds from all users and any accounting error puts all funds at risk. - - So, back to the second iteration, which is the only one so far that is immune from doublespend attack as the funds are already in the destination's CC address. One complication is that due to CC sorting of pubkeys, the address for sending and receiving is the same, so the destination pubkey needs to be attached to each opreturn. - - Now when the prior hashchain value is sent via payment, it allows the receiver to spend the utxo, so the only protection needed is to prevent channel close from invalidating already made payments. - - In order to allow multiple payments included in a single transaction, presentation of the N prior hashchain value can be used to get N payments and all the spends create a spending chain in sequential order of the hashchain. - -*/ - -// 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]; - - GetCCaddress1of2(cp,channeladdr,srcpub,destpub); - GetTokensCCaddress1of2(cp,tokenschanneladdr,srcpub,destpub); - if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && (strcmp(destaddr,channeladdr) == 0 || strcmp(destaddr,tokenschanneladdr) == 0)) - return(tx.vout[v].nValue); - } - return(0); -} - -int64_t IsChannelsMarkervout(struct CCcontract_info *cp,const CTransaction& tx,CPubKey pubkey,int32_t v) -{ - char destaddr[65],ccaddr[65]; - - GetCCaddress(cp,ccaddr,pubkey); - if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,ccaddr) == 0 ) - return(tx.vout[v].nValue); - } - return(0); -} - -CScript EncodeChannelsOpRet(uint8_t funcid,uint256 tokenid,uint256 opentxid,CPubKey srcpub,CPubKey destpub,int32_t numpayments,int64_t payment,uint256 hashchain) -{ - CScript opret; uint8_t evalcode = EVAL_CHANNELS; - vscript_t vopret; - - vopret = E_MARSHAL(ss << evalcode << funcid << opentxid << srcpub << destpub << numpayments << payment << hashchain); - if (tokenid!=zeroid) - { - std::vector pks; - pks.push_back(srcpub); - pks.push_back(destpub); - return(EncodeTokenOpRet(tokenid,pks, std::make_pair(OPRETID_CHANNELSDATA, vopret))); - } - opret << OP_RETURN << vopret; - return(opret); -} - -uint8_t DecodeChannelsOpRet(const CScript &scriptPubKey, uint256 &tokenid, uint256 &opentxid, CPubKey &srcpub,CPubKey &destpub,int32_t &numpayments,int64_t &payment,uint256 &hashchain) -{ - std::vector> oprets; - std::vector vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode; - std::vector pubkeys; - - if (DecodeTokenOpRet(scriptPubKey,tokenevalcode,tokenid,pubkeys,oprets)!=0 && GetOpretBlob(oprets, OPRETID_CHANNELSDATA, vOpretExtra) && tokenevalcode==EVAL_TOKENS && vOpretExtra.size()>0) - { - vopret=vOpretExtra; - } - else GetOpReturnData(scriptPubKey, vopret); - if ( vopret.size() > 2 ) - { - script = (uint8_t *)vopret.data(); - if ( script[0] == EVAL_CHANNELS ) - { - if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> opentxid; ss >> srcpub; ss >> destpub; ss >> numpayments; ss >> payment; ss >> hashchain) != 0 ) - { - return(f); - } - } 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); -} - -bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) -{ - uint256 txid,param3,tokenid; - CPubKey srcpub,destpub; - int32_t param1,numvouts; int64_t param2; uint8_t funcid; - CTransaction vinTx; uint256 hashBlock; int64_t inputs=0,outputs=0; - - if ((numvouts=tx.vout.size()) > 0 && (funcid=DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, param1, param2, param3))!=0) - { - switch (funcid) - { - case 'O': - return (true); - case 'P': - if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); - inputs = vinTx.vout[tx.vin[1].prevout.n].nValue; - outputs = tx.vout[0].nValue + tx.vout[3].nValue; - break; - case 'C': - if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); - inputs = vinTx.vout[tx.vin[1].prevout.n].nValue; - outputs = tx.vout[0].nValue; - break; - case 'R': - if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); - inputs = vinTx.vout[tx.vin[1].prevout.n].nValue; - outputs = tx.vout[2].nValue; - break; - default: - return (false); - } - if ( inputs != outputs ) - { - LOGSTREAM("channelscc",CCLOG_INFO, stream << "inputs " << inputs << " vs outputs " << outputs << std::endl); - return eval->Invalid("mismatched inputs != outputs"); - } - else return (true); - } - else - { - return eval->Invalid("invalid op_return data"); - } - return(false); -} - -bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) -{ - 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]; 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; - - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - if (ChannelsExactAmounts(cp,eval,tx,1,10000) == false ) - { - return eval->Invalid("invalid channel inputs vs. outputs!"); - } - else - { - txid = tx.GetHash(); - 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': - //vin.0: normal input - //vout.0: CC vout for channel funding on CC1of2 pubkey - //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 - 'O' zerotxid senderspubkey receiverspubkey totalnumberofpayments paymentamount hashchain - return eval->Invalid("unexpected ChannelsValidate for channelsopen!"); - case 'P': - //vin.0: normal input - //vin.1: CC input from channel funding - //vin.2: CC input from src marker - //vout.0: CC vout change to CC1of2 pubkey - //vout.1: CC vout marker to senders pubKey - //vout.2: CC vout marker to receiver pubkey - //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 ( 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 (hush_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelopen is not yet confirmed(notarized)!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelpayment!"); - else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 ) - return eval->Invalid("vin."+std::to_string(tx.vin.size()-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 - { - 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!"); - 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 ( 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[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) - return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" 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) - return eval->Invalid("not enough funds in channel for that amount!"); - } - } - break; - case 'C': - //vin.0: normal input - //vin.1: CC input from channel funding - //vin.2: CC input from src marker - //vout.0: CC vout for channel funding - //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 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 (hush_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelopen is not yet confirmed(notarized)!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelclose!"); - else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 ) - return eval->Invalid("vin."+std::to_string(tx.vin.size()-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(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 ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) - return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" 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"); - } - break; - case 'R': - //vin.0: normal input - //vin.1: CC input from channel funding - //vin.2: CC input from src marker - //vout.0: CC vout marker to senders pubKey - //vout.1: CC vout marker to receiver pubKey - //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 ( 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 (hush_txnotarizedconfirmed(opentxid) == 0) - return eval->Invalid("channelopen is not yet confirmed(notarized)!"); - else if (hush_txnotarizedconfirmed(param3) == 0) - return eval->Invalid("channelClose is not yet confirmed(notarized)!"); - else if ( IsCCInput(tx.vin[0].scriptSig) != 0 ) - return eval->Invalid("vin.0 is normal for channelrefund!"); - else if ( IsCCInput(tx.vin[tx.vin.size()-2].scriptSig) == 0 ) - return eval->Invalid("vin."+std::to_string(tx.vin.size()-2)+" 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(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 ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || prevTx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE) - return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" 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!"); - } - break; - default: - fprintf(stderr,"illegal channels funcid.(%c)\n",funcid); - return eval->Invalid("unexpected channels funcid"); - } - } - else return eval->Invalid("unexpected channels missing funcid"); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - LOGSTREAM("channels",CCLOG_INFO, stream << "Channels tx validated" << std::endl); - else fprintf(stderr,"Channels tx invalid\n"); - return(retval); - } - } -} -// end of consensus code - -// helper functions for rpc calls in rpcwallet.cpp - -int64_t AddChannelsInputs(struct CCcontract_info *cp,CMutableTransaction &mtx, CTransaction openTx, uint256 &prevtxid, CPubKey mypk) -{ - char coinaddr[65]; int64_t param2,totalinputs = 0,numvouts; uint256 txid=zeroid,tmp_txid,hashBlock,param3,tokenid; CTransaction tx; int32_t marker,param1; - std::vector > unspentOutputs; - CPubKey srcpub,destpub; - uint8_t myprivkey[32]; - - if ((numvouts=openTx.vout.size()) > 0 && DecodeChannelsOpRet(openTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='O') - { - if (tokenid!=zeroid) GetTokensCCaddress1of2(cp,coinaddr,srcpub,destpub); - else GetCCaddress1of2(cp,coinaddr,srcpub,destpub); - SetCCunspents(unspentOutputs,coinaddr,true); - } - else - { - LOGSTREAM("channelscc",CCLOG_INFO, stream << "invalid channel open txid" << std::endl); - return 0; - } - if (srcpub==mypk) marker=1; - else marker=2; - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - if ( (int32_t)it->first.index==0 && myGetTransaction(it->first.txhash,tx,hashBlock) != 0 && (numvouts=tx.vout.size()) > 0) - { - if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && - (tmp_txid==openTx.GetHash() || tx.GetHash()==openTx.GetHash()) && IsChannelsMarkervout(cp,tx,marker==1?srcpub:destpub,marker)>0 && - (totalinputs=IsChannelsvout(cp,tx,srcpub,destpub,0))>0) - { - txid = it->first.txhash; - break; - } - } - } - if (txid!=zeroid && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,0) != 0) - { - txid=zeroid; - int32_t mindepth=CHANNELS_MAXPAYMENTS; - std::vector tmp_txs; - myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P'); - for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) - { - const CTransaction &txmempool = *it; - const uint256 &hash = txmempool.GetHash(); - - if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)=='P' && - tmp_txid==openTx.GetHash() && param1 < mindepth) - { - txid=hash; - totalinputs=txmempool.vout[0].nValue; - mindepth=param1; - } - } - } - if (txid != zeroid) - { - prevtxid=txid; - mtx.vin.push_back(CTxIn(txid,0,CScript())); - mtx.vin.push_back(CTxIn(txid,marker,CScript())); - Myprivkey(myprivkey); - if (tokenid!=zeroid) CCaddrTokens1of2set(cp,srcpub,destpub,myprivkey,coinaddr); - else CCaddr1of2set(cp,srcpub,destpub,myprivkey,coinaddr); - memset(myprivkey,0,32); - return totalinputs; - } - else return 0; -} - -UniValue ChannelOpen(const CPubKey& pk, uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment, uint256 tokenid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - uint8_t hash[32],hashdest[32]; uint64_t amount,tokens=0,funds; int32_t i; uint256 hashchain,entropy,hentropy; - CPubKey mypk; struct CCcontract_info *cp,*cpTokens,C,CTokens; - - if ( numpayments <= 0 || payment <= 0 || numpayments > CHANNELS_MAXPAYMENTS ) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid ChannelOpen param numpayments." << numpayments << " payment." << payment << " - max_numpayments." << CHANNELS_MAXPAYMENTS); - if (!destpub.IsFullyValid()) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid destination pubkey"); - if (numpayments <1) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid number of payments, must be greater than 0"); - if (payment <1) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0"); - cp = CCinit(&C,EVAL_CHANNELS); - cpTokens = CCinit(&CTokens,EVAL_TOKENS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - funds = numpayments * payment; - if (tokenid!=zeroid) - { - amount=AddNormalinputs(mtx,mypk,txfee+2*CC_MARKER_VALUE,5,pk.IsValid()); - tokens=AddTokenCCInputs(cpTokens, mtx, mypk, tokenid, funds, 64); - } - else amount=AddNormalinputs(mtx,mypk,funds+txfee+2*CC_MARKER_VALUE,64,pk.IsValid()); - 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); - for (i=0; ifunds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk)); - return(FinalizeCCTxExt(pk.IsValid(),0,cp,mtx,mypk,txfee,EncodeChannelsOpRet('O',tokenid,zeroid,mypk,destpub,numpayments,payment,hashchain))); - } - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding funds"); -} - -UniValue ChannelPayment(const CPubKey& pk, uint64_t txfee,uint256 opentxid,int64_t amount, uint256 secret) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CPubKey mypk,srcpub,destpub; uint256 txid,hashchain,gensecret,hashblock,entropy,hentropy,prevtxid,param3,tokenid; - struct CCcontract_info *cp,C; int32_t i,funcid,prevdepth,numvouts,numpayments,totalnumpayments; - int64_t payment,change,funds,param2; - uint8_t hash[32],hashdest[32]; - CTransaction channelOpenTx,prevTx; - - cp = CCinit(&C,EVAL_CHANNELS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - if (amount <1) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid payment amount, must be greater than 0"); - if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); - if ((numvouts=channelOpenTx.vout.size()) > 0 && DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, totalnumpayments, payment, hashchain)=='O') - { - if (mypk != srcpub && mypk != destpub) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "this is not our channel"); - else if (amount % payment != 0 || amount 0) - { - if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && (change=funds-amount)>=0) - { - numpayments=amount/payment; - if (myGetTransaction(prevtxid,prevTx,hashblock) != 0 && (numvouts=prevTx.vout.size()) > 0 && - ((funcid = DecodeChannelsOpRet(prevTx.vout[numvouts-1].scriptPubKey, tokenid, txid, srcpub, destpub, prevdepth, param2, param3)) != 0) && - (funcid == 'P' || funcid=='O')) - { - if (numpayments > prevdepth) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "not enough funds in channel for that amount"); - else if (numpayments == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid amount"); - if (secret!=zeroid) - { - endiancpy(hash, (uint8_t * ) & secret, 32); - for (i = 0; i < totalnumpayments-(prevdepth-numpayments); i++) - { - vcalc_sha256(0, hashdest, hash, 32); - memcpy(hash, hashdest, 32); - } - endiancpy((uint8_t * ) & gensecret, hashdest, 32); - if (gensecret!=hashchain) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid secret supplied"); - } - else - { - hentropy = DiceHashEntropy(entropy,channelOpenTx.vin[0].prevout.hash,channelOpenTx.vin[0].prevout.n,1); - if (prevdepth-numpayments) - { - endiancpy(hash, (uint8_t * ) & hentropy, 32); - for (i = 0; i < prevdepth-numpayments; i++) - { - vcalc_sha256(0, hashdest, hash, 32); - memcpy(hash, hashdest, 32); - } - endiancpy((uint8_t * ) & secret, hashdest, 32); - } - else endiancpy((uint8_t * ) & secret, (uint8_t * ) & hentropy, 32); - } - } - else - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid previous tx"); - 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,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 (FinalizeCCTxExt(pk.IsValid(), 0, cp, mtx, mypk, txfee, EncodeChannelsOpRet('P', tokenid, opentxid, srcpub, destpub, prevdepth-numpayments, numpayments, secret))); - } - else - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); - } - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); -} - -UniValue ChannelClose(const CPubKey& pk, uint64_t txfee,uint256 opentxid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CPubKey mypk,srcpub,destpub; struct CCcontract_info *cp,C; - CTransaction channelOpenTx; - uint256 hashblock,tmp_txid,prevtxid,hashchain,tokenid; - int32_t numvouts,numpayments; - int64_t payment,funds; - - // verify this is one of our outbound channels - cp = CCinit(&C,EVAL_CHANNELS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); - if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain)!='O') - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx"); - if (hush_txnotarizedconfirmed(opentxid)==false) - CCERR_RESULT("channelscc",CCLOG_INFO, stream <<"channelsopen tx not yet confirmed/notarized"); - if (mypk != srcpub) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot close, you are not channel owner"); - if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 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,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 - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); - } - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); -} - -UniValue ChannelRefund(const CPubKey& pk, uint64_t txfee,uint256 opentxid,uint256 closetxid) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CPubKey mypk; struct CCcontract_info *cp,C; int64_t funds,payment,param2; - int32_t i,numpayments,numvouts,param1; - uint256 hashchain,hashblock,txid,prevtxid,param3,tokenid; - CTransaction channelOpenTx,channelCloseTx,prevTx; - CPubKey srcpub,destpub; - - // verify stoptxid and origtxid match and are mine - cp = CCinit(&C,EVAL_CHANNELS); - if ( txfee == 0 ) - txfee = 10000; - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - if (myGetTransaction(closetxid,channelCloseTx,hashblock) == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close txid"); - if ((numvouts=channelCloseTx.vout.size()) < 1 || DecodeChannelsOpRet(channelCloseTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,param1,param2,param3)!='C') - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel close tx"); - if (hush_txnotarizedconfirmed(closetxid)==false) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsclose tx not yet confirmed/notarized"); - if (txid!=opentxid) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "open and close txid are not from same channel"); - if (myGetTransaction(opentxid,channelOpenTx,hashblock) == 0) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open txid"); - if (hush_txnotarizedconfirmed(opentxid)==false) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "channelsopen tx not yet confirmed/notarized"); - if ((numvouts=channelOpenTx.vout.size()) < 1 || DecodeChannelsOpRet(channelOpenTx.vout[numvouts-1].scriptPubKey,tokenid,txid,srcpub,destpub,numpayments,payment,hashchain)!='O') - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid channel open tx"); - if (mypk != srcpub) - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "cannot refund, you are not the channel owner"); - if ( AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 0 ) - { - if ((funds=AddChannelsInputs(cp,mtx,channelOpenTx,prevtxid,mypk)) !=0 && funds>0) - { - if ((myGetTransaction(prevtxid,prevTx,hashblock) != 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,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))); - } - else - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "previous tx is invalid"); - } - else - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding CC inputs"); - } - CCERR_RESULT("channelscc",CCLOG_INFO, stream << "error adding normal inputs"); -} - -UniValue ChannelsList(const CPubKey& pk) -{ - UniValue result(UniValue::VOBJ); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,tmp_txid,param3,tokenid; - CTransaction tx; char myCCaddr[65],str[512],pub[34]; CPubKey mypk,srcpub,destpub; int32_t vout,numvouts,param1; - int64_t nValue,param2; - - cp = CCinit(&C,EVAL_CHANNELS); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - GetCCaddress(cp,myCCaddr,mypk); - SetCCtxids(txids,myCCaddr,true,EVAL_CHANNELS,zeroid,'O'); - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Channels List")); - for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) - { - txid = *it; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 ) - { - if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O') - { - sprintf(str,"%lld payments of %lld satoshi to %s",(long long)param1,(long long)param2,pubkey33_str(pub,(uint8_t *)&destpub)); - result.push_back(Pair(txid.GetHex().data(),str)); - } - } - } - return(result); -} - -UniValue ChannelsInfo(const CPubKey& pk,uint256 channeltxid) -{ - UniValue result(UniValue::VOBJ),array(UniValue::VARR); CTransaction tx,opentx; uint256 txid,tmp_txid,hashBlock,param3,opentxid,hashchain,tokenid; - struct CCcontract_info *cp,C; char CCaddr[65],addr[65],str[512]; int32_t vout,numvouts,param1,numpayments; - int64_t param2,payment; CPubKey srcpub,destpub,mypk; - std::vector txids; std::vector txs; - - cp = CCinit(&C,EVAL_CHANNELS); - mypk = pk.IsValid()?pk:pubkey2pk(Mypubkey()); - - if (myGetTransaction(channeltxid,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && - (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'O')) - { - GetCCaddress1of2(cp,CCaddr,srcpub,destpub); - Getscriptaddress(addr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG); - result.push_back(Pair("result","success")); - result.push_back(Pair("Channel CC address",CCaddr)); - result.push_back(Pair("Destination address",addr)); - result.push_back(Pair("Number of payments",param1)); - if(tokenid!=zeroid) - { - result.push_back(Pair("Token id",tokenid.GetHex().data())); - result.push_back(Pair("Denomination (token satoshi)",i64tostr(param2))); - result.push_back(Pair("Amount (token satoshi)",i64tostr(param1*param2))); - } - else - { - result.push_back(Pair("Denomination (satoshi)",i64tostr(param2))); - result.push_back(Pair("Amount (satoshi)",i64tostr(param1*param2))); - } - GetCCaddress(cp,CCaddr,mypk); - SetCCtxids(txids,CCaddr,true,EVAL_CHANNELS,channeltxid,0); - for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) - { - if (myGetTransaction(*it,tx,hashBlock) != 0 && (numvouts= tx.vout.size()) > 0 && - DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3)!=0 && (tmp_txid==channeltxid || tx.GetHash()==channeltxid)) - txs.push_back(tx); - } - std::vector tmp_txs; - myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P'); - for (std::vector::const_iterator it=tmp_txs.begin(); it!=tmp_txs.end(); it++) - { - const CTransaction &txmempool = *it; - - if ((numvouts=txmempool.vout.size()) > 0 && DecodeChannelsOpRet(txmempool.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'P' && tmp_txid==channeltxid) - txs.push_back(txmempool); - } - for (std::vector::const_iterator it=txs.begin(); it!=txs.end(); it++) - { - tx=*it; - if ((numvouts= tx.vout.size()) > 0 ) - { - UniValue obj(UniValue::VOBJ); - if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,param1,param2,param3) == 'O' && tx.GetHash()==channeltxid) - { - obj.push_back(Pair("Open",tx.GetHash().GetHex().data())); - } - else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'P' && opentxid==channeltxid) - { - if (myGetTransaction(opentxid,opentx,hashBlock) != 0 && (numvouts=opentx.vout.size()) > 0 && - DecodeChannelsOpRet(opentx.vout[numvouts-1].scriptPubKey,tokenid,tmp_txid,srcpub,destpub,numpayments,payment,hashchain) == 'O') - { - Getscriptaddress(str,tx.vout[3].scriptPubKey); - obj.push_back(Pair("Payment",tx.GetHash().GetHex().data())); - obj.push_back(Pair("Number of payments",param2)); - obj.push_back(Pair("Amount",param2*payment)); - obj.push_back(Pair("Destination",str)); - obj.push_back(Pair("Secret",param3.ToString().c_str())); - obj.push_back(Pair("Payments left",param1)); - } - } - else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'C' && opentxid==channeltxid) - { - obj.push_back(Pair("Close",tx.GetHash().GetHex().data())); - } - else if (DecodeChannelsOpRet(tx.vout[numvouts-1].scriptPubKey,tokenid,opentxid,srcpub,destpub,param1,param2,param3) == 'R' && opentxid==channeltxid) - { - Getscriptaddress(str,tx.vout[2].scriptPubKey); - obj.push_back(Pair("Refund",tx.GetHash().GetHex().data())); - obj.push_back(Pair("Amount",param1*param2)); - obj.push_back(Pair("Destination",str)); - } - array.push_back(obj); - } - } - result.push_back(Pair("Transactions",array)); - } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("Error","Channel not found!")); - } - return(result); -} diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp deleted file mode 100644 index 6b0d06075..000000000 --- a/src/cc/dice.cpp +++ /dev/null @@ -1,1871 +0,0 @@ -// Copyright (c) 2016-2023 The Hush developers -// Distributed under the GPLv3 software license, see the accompanying -// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "CCdice.h" - -// timeout - -/* - in order to implement a dice game, we need a source of entropy, reasonably fast completion time and a way to manage the utxos. - - 1. CC vout locks "house" funds with hash(entropy) - 2. bettor submits bet, with entropy, odds, houseid and sends combined amount into another CC vout. - 3. house account sends funds to winner/loser with proof of entropy - 4. if timeout, bettor gets refund - - 2. and 3. can be done in mempool - - The house commits to an entropy value by including the hash of the entropy value in the 'E' transaction. - - To bet, one of these 'E' transactions is used as the first input and its hashed entropy is combined with the unhashed entropy attached to the bet 'B' transaction. - - The house node monitors the 'B' transactions and if it sees one of its own, it creates either a winner 'W' or loser 'L' transaction, with proof of hash of entropy. - - In the even the house node doesnt respond before timeoutblocks, then anybody (including bettor) can undo the bet with funds going back to the house and bettor - - In order for people to play dice, someone (anyone) needs to create a funded dice plan and addfunding with enough utxo to allow players to find one. - -createfunding: - vins.*: normal inputs - vout.0: CC vout for funding - vout.1: owner vout - vout.2: dice marker address vout for easy searching - vout.3: normal change - vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks - -addfunding (entropy): - vins.*: normal inputs - vout.0: CC vout for locked entropy funds - vout.1: tag to owner address for entropy funds - vout.2: normal change - vout.n-1: opreturn 'E' sbits fundingtxid hentropy - -bet: - vin.0: entropy txid from house (must validate vin0 of 'E') - vins.1+: normal inputs - vout.0: CC vout for locked entropy - vout.1: CC vout for locked bet - vout.2: tag for bettor's address (txfee + odds) - vout.3: change - vout.n-1: opreturn 'B' sbits fundingtxid entropy - -loser: - vin.0: normal input - vin.1: betTx CC vout.0 entropy from bet - vin.2: betTx CC vout.1 bet amount from bet - vin.3+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T' - vout.0: funding CC to entropy owner - vout.1: tag to owner address for entropy funds - vout.2: change to fundingpk - vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof - -winner: - same as loser, but vout.2 is winnings - vout.3: change to fundingpk - vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof - -timeout: - same as winner, just without hentropy or proof - -WARNING: there is an attack vector that precludes betting any large amounts, it goes as follows: - 1. do dicebet to get the house entropy revealed - 2. calculate bettor entropy that would win against the house entropy - 3. reorg the chain and make a big bet using the winning entropy calculated in 2. - - In order to mitigate this, the disclosure of the house entropy needs to be delayed beyond a reasonable reorg depth (notarization). It is recommended for production dice game with significant amounts of money to use such a delayed disclosure method. - - Actually a much better solution to this is possible, which allows to retain the realtime response aspect of dice CC, which is critical to its usage. - -What is needed is for the dealer node to track the entropy tx that was already broadcast into the mempool with its entropy revealed. Then before processing a dicebet, it is checked against the already revealed list. If it is found, the dicebet is refunded with proof that a different dicebet was already used to reveal the entropy - - change to hashtables - validate refund - - */ - -#include "../compat/endian.h" - -#define MAX_ENTROPYUSED 8192 -#define DICE_MINUTXOS 15000 -extern int32_t HUSH_INSYNC; - -pthread_mutex_t DICE_MUTEX,DICEREVEALED_MUTEX; - -struct dicefinish_utxo { uint256 txid; int32_t vout; }; - -struct dicefinish_info -{ - struct dicefinish_info *prev,*next; - CTransaction betTx; - uint256 fundingtxid,bettxid,entropyused,txid; - uint64_t sbits; - int64_t winamount; - int32_t iswin,entropyvout,orphaned; - uint32_t bettxid_ready,revealed; - std::string rawtx; - uint8_t funcid; -} *DICEFINISH_LIST; - -struct dicehash_entry -{ - UT_hash_handle hh; - uint256 bettxid; -} *DICEHASH_TABLE; - -struct dice_entropy -{ - UT_hash_handle hh; - uint256 entropyused,bettxid; - CTransaction betTx; - int32_t entropyvout; -} *DICE_ENTROPY; - -struct dicehash_entry *_dicehash_find(uint256 bettxid) -{ - struct dicehash_entry *ptr; - HASH_FIND(hh,DICEHASH_TABLE,&bettxid,sizeof(bettxid),ptr); - return(ptr); -} - -int32_t _dicehash_clear(uint256 bettxid) -{ - struct dicehash_entry *ptr; - HASH_FIND(hh,DICEHASH_TABLE,&bettxid,sizeof(bettxid),ptr); - if ( ptr != 0 ) - { - fprintf(stderr,"delete %s\n",bettxid.GetHex().c_str()); - HASH_DELETE(hh,DICEHASH_TABLE,ptr); - return(0); - } else fprintf(stderr,"hashdelete couldnt find %s\n",bettxid.GetHex().c_str()); - return(-1); -} - -struct dicehash_entry *_dicehash_add(uint256 bettxid) -{ - struct dicehash_entry *ptr; - ptr = (struct dicehash_entry *)calloc(1,sizeof(*ptr)); - ptr->bettxid = bettxid; - HASH_ADD(hh,DICEHASH_TABLE,bettxid,sizeof(bettxid),ptr); - return(ptr); -} - -int32_t _dicerevealed_find(uint256 &oldbettxid,CTransaction &oldbetTx,int32_t &oldentropyvout,uint256 entropyused,uint256 bettxid,int32_t entropyvout) -{ - struct dice_entropy *ptr; - HASH_FIND(hh,DICE_ENTROPY,&entropyused,sizeof(entropyused),ptr); - if ( ptr != 0 ) - { - if ( entropyvout == ptr->entropyvout ) - { - if ( bettxid == ptr->bettxid ) - { - //fprintf(stderr,"identical %s E.%s v.%d\n",bettxid.GetHex().c_str(),entropyused.GetHex().c_str(),entropyvout); - return(entropyvout+1); - } - else - { - fprintf(stderr,"found identical entropy used.%s %s vs %s v.%d vs %d\n",entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),ptr->bettxid.GetHex().c_str(),entropyvout,ptr->entropyvout); - oldbettxid = ptr->bettxid; - oldbetTx = ptr->betTx; - oldentropyvout = ptr->entropyvout; - return(-1); - } - } else fprintf(stderr,"shared entropy.%s vouts %d vs %d\n",entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout); - } - return(0); -} - -struct dice_entropy *_dicerevealed_add(uint256 entropyused,uint256 bettxid,CTransaction betTx,int32_t entropyvout) -{ - struct dice_entropy *ptr; - ptr = (struct dice_entropy *)calloc(1,sizeof(*ptr)); - ptr->entropyused = entropyused; - ptr->bettxid = bettxid; - ptr->betTx = betTx; - ptr->entropyvout = entropyvout; - HASH_ADD(hh,DICE_ENTROPY,entropyused,sizeof(entropyused),ptr); - return(ptr); -} - -int32_t DiceEntropyUsed(CTransaction &oldbetTx,uint256 &oldbettxid,int32_t &oldentropyvout,uint256 entropyused,uint256 bettxid,CTransaction betTx,int32_t entropyvout) -{ - int32_t retval; - oldbettxid = zeroid; - if ( entropyused == zeroid || bettxid == zeroid ) - { - fprintf(stderr,"null entropyused or bettxid\n"); - return(1); - } - pthread_mutex_lock(&DICEREVEALED_MUTEX); - retval = _dicerevealed_find(oldbettxid,oldbetTx,oldentropyvout,entropyused,bettxid,entropyvout); - pthread_mutex_unlock(&DICEREVEALED_MUTEX); - return(retval); -} - -bool mySenddicetransaction(std::string res,uint256 entropyused,int32_t entropyvout,uint256 bettxid,CTransaction betTx,uint8_t funcid,struct dicefinish_info *ptr) -{ - CTransaction tx; int32_t i=0,retval=-1,oldentropyvout; uint256 oldbettxid; CTransaction oldbetTx; - if ( res.empty() == 0 && res.size() > 64 && is_hexstr((char *)res.c_str(),0) > 64 ) - { - if ( DecodeHexTx(tx,res) != 0 ) - { - if ( ptr != 0 ) - ptr->txid = tx.GetHash(); - //fprintf(stderr,"%s\n%s\n",res.c_str(),uint256_str(str,tx.GetHash())); - if ( funcid == 'R' || (retval= DiceEntropyUsed(oldbetTx,oldbettxid,oldentropyvout,entropyused,bettxid,betTx,entropyvout)) >= 0 ) - { - LOCK(cs_main); - if ( myAddtomempool(tx) != 0 ) - { - RelayTransaction(tx); - if ( retval == 0 ) - { - if ( ptr != 0 ) - ptr->revealed = (uint32_t)time(NULL); - pthread_mutex_lock(&DICEREVEALED_MUTEX); - _dicerevealed_add(entropyused,bettxid,betTx,entropyvout); - pthread_mutex_unlock(&DICEREVEALED_MUTEX); - fprintf(stderr,"added.%c to mempool.[%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str()); - } else fprintf(stderr,"rebroadcast.%c to mempool.[%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str()); - return(true); - } - else - { - RelayTransaction(tx); - fprintf(stderr,"rebroadcast.%c and clear [%d] and broadcast entropyused.%s bettxid.%s -> %s\n",funcid,i,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str()); - if ( ptr != 0 ) - { - if ( ptr->rawtx.empty() == 0 ) - ptr->rawtx.clear(); - ptr->txid = zeroid; - } - //fprintf(stderr,"error adding funcid.%c E.%s bet.%s -> %s to mempool, probably Disable replacement feature size.%d\n",funcid,entropyused.GetHex().c_str(),bettxid.GetHex().c_str(),tx.GetHash().GetHex().c_str(),(int32_t)ptr->rawtx.size()); - } - } else fprintf(stderr,"error duplicate entropyused different bettxid\n"); - } else fprintf(stderr,"error decoding hex\n"); - } - return(false); -} - -int32_t dicefinish_utxosget(int32_t &total,struct dicefinish_utxo *utxos,int32_t max,char *coinaddr) -{ - int32_t n = 0; int64_t threshold = 2 * 10000; - total = 0; - std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr,false); - { - LOCK(mempool.cs); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,it->first.txhash,(int32_t)it->first.index) == 0 ) - { - if ( it->second.satoshis < threshold || it->second.satoshis > 10*threshold ) - continue; - total++; - if ( n < max ) - { - if ( utxos != 0 ) - { - utxos[n].txid = it->first.txhash; - utxos[n].vout = (int32_t)it->first.index; - } - n++; - } - } - } - } - total -= n; - return(n); -} - -int32_t dice_betspent(char *debugstr,uint256 bettxid) -{ - CSpentIndexValue value,value2; - CSpentIndexKey key(bettxid,0); - CSpentIndexKey key2(bettxid,1); - if ( GetSpentIndex(key,value) != 0 || GetSpentIndex(key2,value2) != 0 ) - { - //fprintf(stderr,"%s txid.%s already spent\n",debugstr,bettxid.GetHex().c_str()); - return(1); - } - { - //LOCK(mempool.cs); - if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,bettxid,0) != 0 || myIsutxo_spentinmempool(ignoretxid,ignorevin,bettxid,1) != 0 ) - { - fprintf(stderr,"%s bettxid.%s already spent in mempool\n",debugstr,bettxid.GetHex().c_str()); - return(-1); - } - } - return(0); -} - -void dicefinish_delete(struct dicefinish_info *ptr) -{ - pthread_mutex_lock(&DICE_MUTEX); - _dicehash_clear(ptr->bettxid); - pthread_mutex_unlock(&DICE_MUTEX); - DL_DELETE(DICEFINISH_LIST,ptr); - free(ptr); -} - -void *dicefinish(void *_ptr) -{ - std::vector mypk; struct CCcontract_info *cp,C; char name[32],coinaddr[64],CCaddr[64]; std::string res; int32_t newht,newblock,entropyvout,numblocks,lastheight=0,vin0_needed,i,n,m,num,iter,result; struct dicefinish_info *ptr,*tmp; uint32_t now; struct dicefinish_utxo *utxos; uint256 hashBlock,entropyused; CPubKey dicepk; CTransaction betTx,finishTx,tx; - mypk = Mypubkey(); - pubkey2addr(coinaddr,mypk.data()); - cp = CCinit(&C,EVAL_DICE); - dicepk = GetUnspendable(cp,0); - GetCCaddress(cp,CCaddr,GetUnspendable(cp,0)); - fprintf(stderr,"start dicefinish thread %s CCaddr.%s\n",coinaddr,CCaddr); - if ( (newht= HUSH_INSYNC) == 0 ) - sleep(7); - sleep(3); - while ( 1 ) - { - if ( newht != 0 && lastheight != newht ) - { - lastheight = newht; - newblock = 1; - fprintf(stderr,"dicefinish process lastheight.%d <- newht.%d\n",lastheight,newht); - } else newblock = 0; - now = (uint32_t)time(NULL); - for (iter=-1; iter<=1; iter+=2) - { - vin0_needed = 0; - DL_FOREACH_SAFE(DICEFINISH_LIST,ptr,tmp) - { - if ( ptr->iswin != iter ) - continue; - if ( ptr->revealed != 0 && now > ptr->revealed+3600 ) - { - fprintf(stderr,"purge %s\n",ptr->bettxid.GetHex().c_str()); - dicefinish_delete(ptr); - continue; - } - if ( ptr->bettxid_ready == 0 ) - { - if ( myGetTransaction(ptr->bettxid,betTx,hashBlock) != 0 && hashBlock != zeroid ) - ptr->bettxid_ready = (uint32_t)time(NULL); - else if ( mytxid_inmempool(ptr->bettxid) != 0 ) - ptr->bettxid_ready = (uint32_t)time(NULL); - } - else if ( newblock != 0 && (myGetTransaction(ptr->bettxid,betTx,hashBlock) == 0 || now > ptr->bettxid_ready+600) ) - { - fprintf(stderr,"ORPHANED bettxid.%s\n",ptr->bettxid.GetHex().c_str()); - dicefinish_delete(ptr); - continue; - } - else if ( newblock != 0 && ptr->txid != zeroid ) - { - if ( myGetTransaction(ptr->txid,finishTx,hashBlock) == 0 ) - { - ptr->orphaned++; - fprintf(stderr,"ORPHANED.%d finish txid.%s\n",ptr->orphaned,ptr->txid.GetHex().c_str()); - if ( ptr->orphaned < 4 ) - continue; - if ( ptr->rawtx.empty() == 0 ) - ptr->rawtx.clear(); - ptr->txid = zeroid; - unstringbits(name,ptr->sbits); - result = 0; - ptr->orphaned = 0; - res = DiceBetFinish(ptr->funcid,entropyused,entropyvout,&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin,zeroid,-2); - if ( ptr->entropyused == zeroid ) - { - ptr->entropyused = entropyused; - ptr->entropyvout = entropyvout; - } - if ( entropyused != ptr->entropyused || entropyvout != ptr->entropyvout ) - { - fprintf(stderr,"WARNING entropy %s != %s || %d != %d\n",entropyused.GetHex().c_str(),ptr->entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout); - } - if ( result > 0 ) - { - ptr->rawtx = res; - fprintf(stderr,"send refund!\n"); - mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr); - } - dicefinish_delete(ptr); - continue; - } - } - if ( ptr->bettxid_ready != 0 ) - { - if ( now > ptr->bettxid_ready + 2*3600 ) - { - fprintf(stderr,"purge bettxid_ready %s\n",ptr->bettxid.GetHex().c_str()); - dicefinish_delete(ptr); - continue; - } - else if ( newblock != 0 ) - { - if ( ptr->txid != zeroid ) - { - CCduration(numblocks,ptr->txid); - //fprintf(stderr,"duration finish txid.%s\n",ptr->txid.GetHex().c_str()); - if ( numblocks == 0 ) - mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr); - else continue; - } - } - if ( ptr->txid == zeroid ) - vin0_needed++; - } - } - if ( vin0_needed > 0 ) - { - num = 0; -//fprintf(stderr,"iter.%d vin0_needed.%d\n",iter,vin0_needed); - utxos = (struct dicefinish_utxo *)calloc(vin0_needed,sizeof(*utxos)); - if ( (n= dicefinish_utxosget(num,utxos,vin0_needed,coinaddr)) > 0 ) - { -//fprintf(stderr,"iter.%d vin0_needed.%d got %d, num 0.0002 %d\n",iter,vin0_needed,n,num); - m = 0; - DL_FOREACH_SAFE(DICEFINISH_LIST,ptr,tmp) - { - if ( ptr->iswin != iter ) - continue; - if ( ptr->revealed != 0 && time(NULL) > ptr->revealed+3600 ) - { - fprintf(stderr,"purge2 %s\n",ptr->bettxid.GetHex().c_str()); - dicefinish_delete(ptr); - continue; - } - if ( ptr->txid != zeroid ) - { - CCduration(numblocks,ptr->txid); - //fprintf(stderr,"numblocks %s %d\n",ptr->txid.GetHex().c_str(),numblocks); - if ( numblocks > 0 ) - continue; - } - if ( ptr->bettxid_ready != 0 && ptr->rawtx.size() == 0 && dice_betspent((char *)"dicefinish",ptr->bettxid) <= 0 && ptr->txid == zeroid ) - { - unstringbits(name,ptr->sbits); - result = 0; - res = DiceBetFinish(ptr->funcid,entropyused,entropyvout,&result,0,name,ptr->fundingtxid,ptr->bettxid,ptr->iswin,utxos[m].txid,utxos[m].vout); - if ( ptr->entropyused == zeroid ) - { - ptr->entropyused = entropyused; - ptr->entropyvout = entropyvout; - } - if ( entropyused != ptr->entropyused || entropyvout != ptr->entropyvout ) - { - fprintf(stderr,"WARNING entropy %s != %s || %d != %d\n",entropyused.GetHex().c_str(),ptr->entropyused.GetHex().c_str(),entropyvout,ptr->entropyvout); - } - if ( result > 0 ) - { - ptr->rawtx = res; - mySenddicetransaction(ptr->rawtx,ptr->entropyused,ptr->entropyvout,ptr->bettxid,ptr->betTx,ptr->funcid,ptr); - } - else if ( result != -2 ) - { - fprintf(stderr,"error doing the dicefinish %d of %d process %s %s using %s/v%d need %.8f\n",m,n,iter<0?"loss":"win",ptr->bettxid.GetHex().c_str(),utxos[m].txid.GetHex().c_str(),utxos[m].vout,(double)(iter<0 ? 0 : ptr->winamount)/COIN); - if ( ptr->rawtx.empty() == 0 ) - ptr->rawtx.clear(); - ptr->txid = zeroid; - } - if ( ++m >= n ) - break; - } - else - { - //fprintf(stderr,"error ready.%d dicefinish %d of %d process %s %s using need %.8f finish.%s size.%d betspent.%d\n",ptr->bettxid_ready,m,n,iter<0?"loss":"win",ptr->bettxid.GetHex().c_str(),(double)(iter<0 ? 0 : ptr->winamount)/COIN,ptr->txid.GetHex().c_str(),(int32_t)ptr->rawtx.size(),dice_betspent((char *)"dicefinish",ptr->bettxid)); - } - } - } else if ( system("cc/dapps/sendmany100") != 0 ) - fprintf(stderr,"error issing cc/dapps/sendmany100\n"); - free(utxos); - } - } - if ( (newht= HUSH_INSYNC) == 0 || newht == lastheight ) - sleep(3); - else usleep(500000); - } - return(0); -} - -void DiceQueue(int32_t iswin,uint64_t sbits,uint256 fundingtxid,uint256 bettxid,CTransaction betTx,int32_t entropyvout) -{ - static int32_t didinit; - struct dicefinish_info *ptr; int32_t i,duplicate=0; uint64_t txfee = 10000; - if ( didinit == 0 ) - { - if ( pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dicefinish,0) == 0 ) - { - pthread_mutex_init(&DICE_MUTEX,NULL); - pthread_mutex_init(&DICEREVEALED_MUTEX,NULL); - didinit = 1; - } - else - { - fprintf(stderr,"error launching dicefinish thread\n"); - return; - } - } - //if ( dice_betspent((char *)"DiceQueue",bettxid) != 0 ) - // return; - pthread_mutex_lock(&DICE_MUTEX); - if ( _dicehash_find(bettxid) == 0 ) - { - _dicehash_add(bettxid); - ptr = (struct dicefinish_info *)calloc(1,sizeof(*ptr)); - ptr->fundingtxid = fundingtxid; - ptr->bettxid = bettxid; - ptr->betTx = betTx; - ptr->sbits = sbits; - ptr->iswin = iswin; - ptr->winamount = betTx.vout[1].nValue * ((betTx.vout[2].nValue - txfee)+1); - ptr->entropyvout = entropyvout; - DL_APPEND(DICEFINISH_LIST,ptr); - fprintf(stderr,"queued %dx iswin.%d %.8f -> %.8f %s\n",(int32_t)(betTx.vout[2].nValue - txfee),iswin,(double)betTx.vout[1].nValue/COIN,(double)ptr->winamount/COIN,bettxid.GetHex().c_str()); - } - else - { - //fprintf(stderr,"DiceQueue status bettxid.%s already in list\n",bettxid.GetHex().c_str()); - //_dicehash_clear(bettxid); - } - pthread_mutex_unlock(&DICE_MUTEX); -} - -CPubKey DiceFundingPk(CScript scriptPubKey) -{ - CPubKey pk; uint8_t *ptr,*dest; int32_t i; - if ( scriptPubKey.size() == 35 ) - { - dest = (uint8_t *)pk.begin(); - for (i=0; i<33; i++) - dest[i] = scriptPubKey[i+1]; - } else fprintf(stderr,"DiceFundingPk invalid size.%d\n",(int32_t)scriptPubKey.size()); - return(pk); -} - -uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv,int32_t vout,int32_t usevout) -{ - int32_t i; uint8_t _entropy[32],_hentropy[32]; bits256 tmp256,txidpub,txidpriv,mypriv,mypub,ssecret,ssecret2; uint256 hentropy; - memset(&hentropy,0,32); - endiancpy(txidpriv.bytes,(uint8_t *)&_txidpriv,32); - if ( usevout != 0 ) - { - txidpriv.bytes[1] ^= (vout & 0xff); - txidpriv.bytes[2] ^= ((vout>>8) & 0xff); - } - txidpriv.bytes[0] &= 0xf8, txidpriv.bytes[31] &= 0x7f, txidpriv.bytes[31] |= 0x40; - txidpub = curve25519(txidpriv,curve25519_basepoint9()); - - Myprivkey(tmp256.bytes); - vcalc_sha256(0,mypriv.bytes,tmp256.bytes,32); - mypriv.bytes[0] &= 0xf8, mypriv.bytes[31] &= 0x7f, mypriv.bytes[31] |= 0x40; - mypub = curve25519(mypriv,curve25519_basepoint9()); - - ssecret = curve25519(mypriv,txidpub); - ssecret2 = curve25519(txidpriv,mypub); - if ( memcmp(ssecret.bytes,ssecret2.bytes,32) == 0 ) - { - vcalc_sha256(0,(uint8_t *)&_entropy,ssecret.bytes,32); - vcalc_sha256(0,(uint8_t *)&_hentropy,_entropy,32); - endiancpy((uint8_t *)&entropy,_entropy,32); - endiancpy((uint8_t *)&hentropy,_hentropy,32); - } - else - { - for (i=0; i<32; i++) - fprintf(stderr,"%02x",ssecret.bytes[i]); - fprintf(stderr," ssecret\n"); - for (i=0; i<32; i++) - fprintf(stderr,"%02x",ssecret2.bytes[i]); - fprintf(stderr," ssecret2 dont match\n"); - } - memset(tmp256.bytes,0,32); - //char str[65],str2[65]; - //fprintf(stderr,"generated house hentropy.%s <- entropy.%s\n",uint256_str(str,hentropy),uint256_str(str2,entropy)); - return(hentropy); -} - -int32_t dice_5nibbles(uint8_t *fivevals) -{ - return(((int32_t)fivevals[0]<<16) + ((int32_t)fivevals[1]<<12) + ((int32_t)fivevals[2]<<8) + ((int32_t)fivevals[3]<<4) + ((int32_t)fivevals[4])); -} - -uint64_t DiceCalc(int64_t bet,int64_t vout2,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks,uint256 houseentropy,uint256 bettorentropy) -{ - uint8_t buf[64],_house[32],_bettor[32],_hash[32],hash[32],hash16[64]; uint64_t odds,winnings; arith_uint256 house,bettor; char str[65],str2[65]; int32_t i,modval; - if ( vout2 <= 10000 ) - { - fprintf(stderr,"unexpected vout2.%llu\n",(long long)vout2); - return(0); - } - else odds = (vout2 - 10000); - if ( bet < minbet || bet > maxbet ) - { - CCerror = strprintf("bet size violation %.8f",(double)bet/COIN); - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(0); - } - if ( odds > maxodds ) - { - CCerror = strprintf("invalid odds %d, must be <= %d",odds, maxodds); - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(0); - } - - endiancpy(buf,(uint8_t *)&houseentropy,32); - endiancpy(&buf[32],(uint8_t *)&bettorentropy,32); - vcalc_sha256(0,(uint8_t *)&_house,buf,64); - endiancpy((uint8_t *)&house,_house,32); - - endiancpy(buf,(uint8_t *)&bettorentropy,32); - endiancpy(&buf[32],(uint8_t *)&houseentropy,32); - vcalc_sha256(0,(uint8_t *)&_bettor,buf,64); - endiancpy((uint8_t *)&bettor,_bettor,32); - winnings = 0; - //fprintf(stderr,"calc house entropy %s vs bettor %s\n",uint256_str(str,*(uint256 *)&house),uint256_str(str2,*(uint256 *)&bettor)); - if ( odds > 1 ) - { - if ( 0 ) - { // old way - bettor = (bettor / arith_uint256(odds)); - if ( bettor >= house ) - winnings = bet * (odds+1); - return(winnings); - } - if ( odds > 9999 ) // shouldnt happen - return(0); - endiancpy(buf,(uint8_t *)&house,32); - endiancpy(&buf[32],(uint8_t *)&bettor,32); - vcalc_sha256(0,(uint8_t *)&_hash,buf,64); - endiancpy(hash,_hash,32); - for (i=0; i<32; i++) - { - hash16[i<<1] = ((hash[i] >> 4) & 0x0f); - hash16[(i<<1) + 1] = (hash[i] & 0x0f); - } - modval = 0; - for (i=0; i<12; i++) - { - modval = dice_5nibbles(&hash16[i*5]); - if ( modval < 1000000 ) - { - modval %= 10000; - break; - } - } - //fprintf(stderr,"modval %d vs %d\n",modval,(int32_t)(10000/(odds+1))); - if ( modval < 10000/(odds+1) ) - winnings = bet * (odds+1); - } - else if ( bettor >= house ) - winnings = bet * (odds+1); - return(winnings); -} - -CScript EncodeDiceFundingOpRet(uint8_t funcid,uint64_t sbits,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks) -{ - CScript opret; uint8_t evalcode = EVAL_DICE; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << 'F' << sbits << minbet << maxbet << maxodds << timeoutblocks); - return(opret); -} - -uint8_t DecodeDiceFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks) -{ - std::vector vopret; uint8_t *script,e,f; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 ) - { - if ( e == EVAL_DICE && f == 'F' ) - return(f); - } - return(0); -} - -CScript EncodeDiceOpRet(uint8_t funcid,uint64_t sbits,uint256 fundingtxid,uint256 hash,uint256 proof) -{ - CScript opret; uint8_t evalcode = EVAL_DICE; - opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << sbits << fundingtxid << hash << proof); - return(opret); -} - -uint8_t DecodeDiceOpRet(uint256 txid,const CScript &scriptPubKey,uint64_t &sbits,uint256 &fundingtxid,uint256 &hash,uint256 &proof) -{ - std::vector vopret; uint8_t *script,e,f,funcid; int64_t minbet,maxbet,maxodds,timeoutblocks; - //script = (uint8_t *)scriptPubKey.data(); - //fprintf(stderr,"decode %02x %02x %02x\n",script[0],script[1],script[2]); - GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 )//&& script[0] == 0x6a ) - { - script = (uint8_t *)vopret.data(); - if ( script[0] == EVAL_DICE ) - { - if ( script[1] == 'F' ) - { - if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> minbet; ss >> maxbet; ss >> maxodds; ss >> timeoutblocks) != 0 ) - { - memset(&hash,0,32); - fundingtxid = txid; - return('F'); - } else fprintf(stderr,"unmarshal error for F\n"); - } - else if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> fundingtxid; ss >> hash; ss >> proof) != 0 ) - { - if ( e == EVAL_DICE && (f == 'R' || f == 'B' || f == 'W' || f == 'L' || f == 'T' || f == 'E') ) - return(f); - else fprintf(stderr,"mismatched e.%02x f.(%c)\n",e,f); - } - } else fprintf(stderr,"script[0] %02x != EVAL_DICE\n",script[0]); - } else fprintf(stderr,"not enough opret.[%d]\n",(int32_t)vopret.size()); - return(0); -} - -uint256 DiceGetEntropy(CTransaction tx,uint8_t reffuncid) -{ - uint256 hash,fundingtxid,proof; uint64_t sbits; int32_t numvouts; - if ( (numvouts= tx.vout.size()) > 0 && DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof) == reffuncid ) - return(hash); - else return(zeroid); -} - -uint64_t IsDicevout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v,uint64_t refsbits,uint256 reffundingtxid) -{ - char destaddr[64]; uint8_t funcid; int32_t numvouts; uint64_t sbits; uint256 fundingtxid,hash,proof; - if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 && (numvouts= tx.vout.size()) > 0 ) - { - if ( (funcid= DecodeDiceOpRet(tx.GetHash(),tx.vout[numvouts-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 && sbits == refsbits && ((funcid == 'F' && tx.GetHash() == reffundingtxid) || fundingtxid == reffundingtxid) ) - return(tx.vout[v].nValue); - } - } - return(0); -} - -int64_t DiceAmounts(uint64_t &inputs,uint64_t &outputs,struct CCcontract_info *cp,Eval *eval,const CTransaction &tx,uint64_t refsbits,uint256 reffundingtxid) -{ - CTransaction vinTx; uint256 hashBlock; int32_t i,numvins,numvouts; uint64_t assetoshis; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - inputs = outputs = 0; - for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) - { - if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("always should find vin, but didnt"); - else - { - if ( (assetoshis= IsDicevout(cp,vinTx,(int32_t)tx.vin[i].prevout.n,refsbits,reffundingtxid)) != 0 ) - inputs += assetoshis; - } - } - } - for (i=0; i 1 && DiceIsmine(vinTx.vout[1].scriptPubKey) != 0 && vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( ((funcid= DecodeDiceOpRet(txid,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,hentropy,vinproof)) == 'E' || funcid == 'W' || funcid == 'L') && sbits == vinsbits && fundingtxid == vinfundingtxid ) - { - hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash,vinTx.vin[0].prevout.n,0); - entropyvout = vinTx.vin[0].prevout.n; - //fprintf(stderr,"bettxid %s -> vin0 %s/v%d -> %s\n",txid.GetHex().c_str(),vinTx.vin[0].prevout.hash.GetHex().c_str(),entropyvout,entropy.GetHex().c_str()); - if ( hentropy != hentropy2 ) - { - hentropy2 = DiceHashEntropy(entropy,vinTx.vin[0].prevout.hash,vinTx.vin[0].prevout.n,1); - //fprintf(stderr,"alt bettxid %s -> vin0 %s/v%d -> %s\n",txid.GetHex().c_str(),vinTx.vin[0].prevout.hash.GetHex().c_str(),entropyvout,entropy.GetHex().c_str()); - } - if ( hentropy == hentropy2 ) - { - winnings = DiceCalc(tx.vout[1].nValue,tx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,entropy,bettorentropy); - //char str[65]; fprintf(stderr,"%s winnings %.8f bet %.8f at odds %d:1\n",uint256_str(str,tx.GetHash()),(double)winnings/COIN,(double)tx.vout[1].nValue/COIN,(int32_t)(tx.vout[2].nValue-10000)); - //fprintf(stderr,"I am house entropy %.8f entropy.(%s) vs %s -> winnings %.8f\n",(double)vinTx.vout[0].nValue/COIN,uint256_str(str,entropy),uint256_str(str2,hash),(double)winnings/COIN); - if ( winnings == 0 ) - { - // queue 'L' losing tx - return(-1); - } - else - { - // queue 'W' winning tx - return(1); - } - } - else - { - fprintf(stderr,"both hentropy != hentropy2\n"); - } - } else fprintf(stderr,"funcid.%c sbits %llx vs %llx cmp.%d\n",funcid,(long long)sbits,(long long)vinsbits,fundingtxid == vinfundingtxid); - } //else fprintf(stderr,"notmine.%d or not CC.%d\n",DiceIsmine(vinTx.vout[1].scriptPubKey) != 0,vinTx.vout[0].scriptPubKey.IsPayToCryptoCondition() != 0); - return(0); -} - -bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks) -{ - int32_t numblocks; - if ( CCduration(numblocks,betTx.GetHash()) <= 0 ) - return(false); - return(numblocks >= timeoutblocks); -} - -bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) -{ - uint256 txid,fundingtxid,vinfundingtxid,vinhentropy,vinproof,hashBlock,hash,proof,entropy; int64_t minbet,maxbet,maxodds,timeoutblocks,odds,winnings; uint64_t vinsbits,refsbits=0,sbits,amount,inputs,outputs,txfee=10000; int32_t numvins,entropyvout,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript fundingPubKey; CTransaction fundingTx,vinTx,vinofvinTx; char CCaddr[64]; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - txid = tx.GetHash(); - if ( (funcid= DecodeDiceOpRet(txid,tx.vout[numvouts-1].scriptPubKey,refsbits,fundingtxid,hash,proof)) != 0 ) - { - if ( eval->GetTxUnconfirmed(fundingtxid,fundingTx,hashBlock) == 0 ) - return eval->Invalid("cant find fundingtxid"); - else if ( fundingTx.vout.size() > 0 && DecodeDiceFundingOpRet(fundingTx.vout[fundingTx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 'F' ) - return eval->Invalid("fundingTx not valid"); - if ( maxodds > 9999 ) - return eval->Invalid("maxodds too big"); - fundingPubKey = fundingTx.vout[1].scriptPubKey; - if ( sbits != refsbits ) - { - fprintf(stderr,"VALIDATION ERROR: sbits %llx != refsbits %llx\n",(long long)sbits,(long long)refsbits); - //return eval->Invalid("mismatched diceplan"); - } - switch ( funcid ) - { - case 'F': - //vins.*: normal inputs - //vout.0: CC vout for funding - //vout.1: normal marker vout for easy searching - //vout.2: normal change - //vout.n-1: opreturn 'F' sbits minbet maxbet maxodds timeoutblocks - return eval->Invalid("unexpected DiceValidate for createfunding"); - break; - case 'E': // check sig of vin to match fundingtxid in the 'B' tx - //vins.*: normal inputs - //vout.0: CC vout for locked entropy funds - //vout.1: tag to owner address for entropy funds - //vout.2: normal change - //vout.n-1: opreturn 'E' sbits fundingtxid hentropy - return eval->Invalid("unexpected DiceValidate for addfunding entropy"); - break; - case 'B': - //vin.0: entropy txid from house - //vins.1+: normal inputs - //vout.0: CC vout for locked entropy - //vout.1: CC vout for locked bet - //vout.2: tag for bettor's address (txfee + odds) - //vout.3: change - //vout.n-1: opreturn 'B' sbits fundingtxid entropy - preventCCvouts = 2; - preventCCvins = 1; - if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) - return eval->Invalid("vin.0 is normal for bet"); - else if ( tx.vout[0].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.0 is normal for bet"); - else if ( tx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 ) - return eval->Invalid("vout.1 is normal for bet"); - else if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("always should find vin.0, but didnt for bet"); - else if ( vinTx.vout[1].scriptPubKey != fundingPubKey ) - return eval->Invalid("entropy tx not fundingPubKey for bet"); - else if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,(int64_t)vinTx.vout[tx.vin[0].prevout.n].nValue) == 0 ) - { - fprintf(stderr,"%s prevout.%d %.8f\n",tx.vin[0].prevout.hash.GetHex().c_str(),(int32_t)tx.vin[0].prevout.n,(double)vinTx.vout[tx.vin[0].prevout.n].nValue/COIN); - return eval->Invalid("vout[0] != entropy nValue for bet"); - } - else if ( ConstrainVout(tx.vout[1],1,cp->unspendableCCaddr,0) == 0 ) - return eval->Invalid("vout[1] constrain violation for bet"); - else if ( tx.vout[2].nValue > txfee+maxodds || tx.vout[2].nValue <= txfee ) - return eval->Invalid("vout[2] nValue violation for bet"); - else if ( eval->GetTxUnconfirmed(vinTx.vin[0].prevout.hash,vinofvinTx,hashBlock) == 0 || vinofvinTx.vout.size() < 1 ) - return eval->Invalid("always should find vinofvin.0, but didnt for bet"); - else if ( vinTx.vin[0].prevout.hash != fundingtxid ) - { - if ( (int32_t)vinTx.vin[0].prevout.n < 0 || vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) - { - uint8_t *ptr0,*ptr1; int32_t i; char str[65],addr0[64],addr1[64]; - Getscriptaddress(addr0,vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey); - Getscriptaddress(addr1,fundingPubKey); - if ( strcmp(addr0,addr1) != 0 ) - { - fprintf(stderr,"%s != %s betTx.%s\n",addr0,addr1,uint256_str(str,txid)); - fprintf(stderr,"entropyTx.%s v%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n); - fprintf(stderr,"entropyTx vin0 %s v%d\n",uint256_str(str,vinTx.vin[0].prevout.hash),(int32_t)vinTx.vin[0].prevout.n); - ptr0 = (uint8_t *)&vinofvinTx.vout[vinTx.vin[0].prevout.n].scriptPubKey[0]; - ptr1 = (uint8_t *)&fundingPubKey[0]; - for (i=0; iInvalid("vin1 of entropy tx not fundingPubKey for bet"); - } - } - } - if ( (iswin= DiceIsWinner(entropy,entropyvout,txid,tx,vinTx,hash,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) - { - // will only happen for fundingPubKey - if ( HUSH_INSYNC != 0 && HUSH_DEALERNODE != 0 ) - DiceQueue(iswin,sbits,fundingtxid,txid,tx,entropyvout); - } - else - { - //fprintf(stderr,"why does node1 get VALIDATION ERROR: invalid dicebet bettxid %s\n",txid.GetHex().c_str()); - //return eval->Invalid("invalid dicebet bettxid"); - } - break; - // make sure all funding txid are from matching sbits and fundingtxid!! - case 'L': - case 'W': - case 'T': - //vin.0: normal input - //vin.1: betTx CC vout.0 entropy from bet - //vin.2: betTx CC vout.1 bet amount from bet - //vin.3+: funding CC vout.0 from 'F', 'E', 'W', 'L' or 'T' - //vout.1: tag to owner address for entropy funds - preventCCvouts = 1; - DiceAmounts(inputs,outputs,cp,eval,tx,sbits,fundingtxid); - if ( IsCCInput(tx.vin[1].scriptSig) == 0 || IsCCInput(tx.vin[2].scriptSig) == 0 ) - return eval->Invalid("vin0 or vin1 normal vin for bet"); - else if ( tx.vin[1].prevout.hash != tx.vin[2].prevout.hash ) - return eval->Invalid("vin0 != vin1 prevout.hash for bet"); - else if ( eval->GetTxUnconfirmed(tx.vin[1].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("always should find vin.0, but didnt for wlt"); - else if ( vinTx.vout.size() < 3 || DecodeDiceOpRet(tx.vin[1].prevout.hash,vinTx.vout[vinTx.vout.size()-1].scriptPubKey,vinsbits,vinfundingtxid,vinhentropy,vinproof) != 'B' ) - return eval->Invalid("not betTx for vin0/1 for wlt"); - else if ( sbits != vinsbits || fundingtxid != vinfundingtxid ) - return eval->Invalid("sbits or fundingtxid mismatch for wlt"); - else if ( fundingPubKey != tx.vout[1].scriptPubKey ) - return eval->Invalid("tx.vout[1] != fundingPubKey for wlt"); - if ( funcid == 'L' ) - { - //vout.0: funding CC to entropy owner - //vout.n-1: opreturn 'L' sbits fundingtxid hentropy proof - if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,inputs) == 0 ) - return eval->Invalid("vout[0] != inputs-txfee for loss"); - else if ( tx.vout[2].scriptPubKey != fundingPubKey ) - { - if ( tx.vout[2].scriptPubKey.size() == 0 || tx.vout[2].scriptPubKey[0] != 0x6a ) - return eval->Invalid("vout[2] not send to fundingPubKey for loss"); - } - iswin = -1; - } - else - { - //vout.0: funding CC change to entropy owner - //vout.2: normal output to bettor's address - //vout.n-1: opreturn 'W' sbits fundingtxid hentropy proof - odds = vinTx.vout[2].nValue - txfee; - if ( ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) == 0 ) - return eval->Invalid("vout[0] != inputs-txfee for win/timeout"); - else if ( tx.vout[2].scriptPubKey != vinTx.vout[2].scriptPubKey ) - return eval->Invalid("vout[2] scriptPubKey mismatch for win/timeout"); - else if ( tx.vout[2].nValue != (odds+1)*vinTx.vout[1].nValue ) - return eval->Invalid("vout[2] payut mismatch for win/timeout"); - else if ( inputs != (outputs + tx.vout[2].nValue) && inputs != (outputs + tx.vout[2].nValue+txfee) ) - { - fprintf(stderr,"inputs %.8f != outputs %.8f (%.8f %.8f %.8f %.8f)\n",(double)inputs/COIN,(double)outputs/COIN,(double)tx.vout[0].nValue/COIN,(double)tx.vout[1].nValue/COIN,(double)tx.vout[2].nValue/COIN,(double)tx.vout[3].nValue/COIN); - return eval->Invalid("CC funds mismatch for win/timeout"); - } - else if ( tx.vout[3].scriptPubKey != fundingPubKey ) - { - if ( tx.vout[3].scriptPubKey.size() == 0 || tx.vout[3].scriptPubKey[0] != 0x6a ) - return eval->Invalid("vout[3] not send to fundingPubKey for win/timeout"); - } - iswin = (funcid == 'W'); - } - if ( iswin != 0 ) - { - //char str[65],str2[65]; - entropy = DiceGetEntropy(vinTx,'B'); - vcalc_sha256(0,(uint8_t *)&hash,(uint8_t *)&proof,32); - //fprintf(stderr,"calculated house hentropy.%s\n",uint256_str(str,hash)); - //fprintf(stderr,"verify house entropy %s vs bettor %s\n",uint256_str(str,proof),uint256_str(str2,entropy)); - winnings = DiceCalc(vinTx.vout[1].nValue,vinTx.vout[2].nValue,minbet,maxbet,maxodds,timeoutblocks,proof,entropy); - if ( (winnings == 0 && iswin > 0) || (winnings > 0 && iswin < 0) ) - return eval->Invalid("DiceCalc mismatch for win/loss"); - } - else if ( DiceVerifyTimeout(vinTx,timeoutblocks) == 0 ) - return eval->Invalid("invalid timeout claim for timeout"); - break; - case 'R': - if ( eval->GetTxUnconfirmed(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("always should find vin.0, but didnt for refund"); - else if ( vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) - { - char fundingaddr[64],cmpaddr[64]; - Getscriptaddress(fundingaddr,fundingPubKey); - Getscriptaddress(cmpaddr,vinTx.vout[tx.vin[0].prevout.n].scriptPubKey); - if ( strcmp(cmpaddr,fundingaddr) != 0 ) - { - fprintf(stderr,"cmpaddr.%s != fundingaddr.%s\n",cmpaddr,fundingaddr); - return eval->Invalid("vin.0 not from fundingPubKey for refund"); - } - } - if ( (rand() % 1000) == 0 ) - fprintf(stderr,"add more validation for refunds\n"); - break; - default: - fprintf(stderr,"illegal dice funcid.(%c)\n",funcid); - return eval->Invalid("unexpected dice funcid"); - break; - } - } else return eval->Invalid("unexpected dice missing funcid"); - return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts)); - } - return(true); -} - -uint64_t AddDiceInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,uint64_t total,int32_t maxinputs,uint64_t refsbits,uint256 reffundingtxid) -{ - char coinaddr[64],str[65]; uint64_t threshold,sbits,nValue,totalinputs = 0; uint256 txid,hash,proof,hashBlock,fundingtxid; CTransaction tx; int32_t j,vout,n = 0; uint8_t funcid; - std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr,true); - if ( maxinputs > CC_MAXVINS ) - maxinputs = CC_MAXVINS; - if ( maxinputs > 0 ) - threshold = total / maxinputs; - else threshold = total; - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - if ( vout != 0 || it->second.satoshis < threshold ) - continue; - //fprintf(stderr,"(%s) %s/v%d %.8f\n",coinaddr,uint256_str(str,txid),vout,(double)it->second.satoshis/COIN); - for (j=0; j 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) - { - if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 ) - { - char str[65],sstr[16]; - unstringbits(sstr,sbits); - if ( sbits == refsbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) - { - if ( funcid == 'R' || funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T' ) - { - if ( total != 0 && maxinputs != 0 ) - { - if ( funcid == 'R' ) - fprintf(stderr,">>>>>>>>>>>> use (%c) %.8f %s %s/v%d\n",funcid,(double)tx.vout[0].nValue/COIN,sstr,uint256_str(str,txid),vout); - mtx.vin.push_back(CTxIn(txid,vout,CScript())); - } - totalinputs += it->second.satoshis; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } else fprintf(stderr,"null funcid\n"); - } - } - return(totalinputs); -} - -int64_t DicePlanFunds(uint64_t &entropyval,uint256 &entropytxid,uint64_t refsbits,struct CCcontract_info *cp,CPubKey dicepk,uint256 reffundingtxid, int32_t &entropytxs,bool random) -{ - char coinaddr[64],str[65]; uint64_t sbits; int64_t nValue,sum,totalinputs = 0; uint256 hash,txid,proof,hashBlock,fundingtxid; CScript fundingPubKey; CTransaction tx,vinTx; int32_t vout,first=0,n=0,i=0,pendingbets=0; uint8_t funcid; - std::vector > unspentOutputs; - entropyval = 0; - entropytxid = zeroid; - if ( myGetTransaction(reffundingtxid,tx,hashBlock) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,0) != 0 ) - { - fundingPubKey = tx.vout[1].scriptPubKey; - } else return(0); - GetCCaddress(cp,coinaddr,dicepk); - SetCCunspents(unspentOutputs,coinaddr,true); - entropyval = 0; - int loops = 0; - int numtxs = unspentOutputs.size()/2; - int startfrom = rand() % (numtxs+1); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - if ( vout != 0 ) - continue; - sum += it->second.satoshis; - loops++; - if (random) { - if ( loops < startfrom ) - continue; - if ( (rand() % 100) < 90 ) - continue; - } - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( (funcid= DecodeDiceOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof)) != 0 && sbits == refsbits ) - { - if ( funcid == 'B' ) - { - pendingbets++; - fprintf(stderr,"%d: %s/v%d (%c %.8f) %.8f %.8f\n",n,uint256_str(str,txid),vout,funcid,(double)it->second.satoshis/COIN,(double)totalinputs/COIN,(double)sum/COIN); - } - if ( (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) - { - //fprintf(stderr,"%d: %s/v%d (%c %.8f) %.8f %.8f\n",n,uint256_str(str,txid),vout,funcid,(double)it->second.satoshis/COIN,(double)totalinputs/COIN,(double)sum/COIN); - if ( (nValue= IsDicevout(cp,tx,vout,refsbits,reffundingtxid)) >= 10000 && (funcid == 'R' || funcid == 'F' || funcid == 'E' || funcid == 'W' || funcid == 'L' || funcid == 'T') ) - { - if ( funcid == 'L' || funcid == 'W' || funcid == 'E' ) - n++; - totalinputs += nValue; - if ( first == 0 && (funcid == 'E' || funcid == 'W' || funcid == 'L') ) - { - //fprintf(stderr,"check first\n"); - if ( tx.vout.size() > 1 && fundingPubKey == tx.vout[1].scriptPubKey ) - { - if ( myGetTransaction(tx.vin[0].prevout.hash,vinTx,hashBlock) == 0 || (int32_t)tx.vin[0].prevout.n < 0 ) - { - fprintf(stderr,"cant find entropy vin0 %s or vin0prev %d vouts[%d], iscoinbase.%d\n",uint256_str(str,tx.vin[0].prevout.hash),(int32_t)tx.vin[0].prevout.n,(int32_t)vinTx.vout.size(),(int32_t)vinTx.vin.size()); - continue; - } - if ( (int32_t)vinTx.vin[0].prevout.n < 0 || vinTx.vout.size() < 2 ) - { - fprintf(stderr,"skip coinbase or strange entropy tx\n"); - continue; - } - //if ( fundingtxid != tx.vin[0].prevout.hash && vinTx.vout[tx.vin[0].prevout.n].scriptPubKey != fundingPubKey ) - if ( fundingtxid != tx.vin[0].prevout.hash && vinTx.vout[1].scriptPubKey != fundingPubKey ) - { - uint8_t *ptr0,*ptr1; int32_t i; char str[65],addr0[64],addr1[64]; - Getscriptaddress(addr0,vinTx.vout[1].scriptPubKey); - Getscriptaddress(addr1,fundingPubKey); - if ( strcmp(addr0,addr1) != 0 ) - { - ptr0 = (uint8_t *)&vinTx.vout[1].scriptPubKey[0]; - ptr1 = (uint8_t *)&fundingPubKey[0]; - for (i=0; i txids; - GetCCaddress(cp,CCaddr,dicepk); - SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F'); - if ( fundingtxid != zeroid ) // avoid scan unless creating new funding plan - { - //fprintf(stderr,"check fundingtxid\n"); - if ( myGetTransaction(fundingtxid,tx,hashBlock) != 0 && tx.vout.size() > 1 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) - { - if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' && sbits == refsbits ) - { - fundingPubKey = tx.vout[1].scriptPubKey; - return(true); - } else fprintf(stderr,"error decoding opret or sbits mismatch %llx vs %llx\n",(long long)sbits,(long long)refsbits); - } else fprintf(stderr,"couldnt get funding tx\n"); - return(false); - } - for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) - { - txid = *it; - if ( fundingtxid != zeroid && txid != fundingtxid ) - continue; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout.size() > 0 && ConstrainVout(tx.vout[0],1,CCaddr,0) != 0 ) - { - if ( DecodeDiceFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 'F' ) - { - if ( sbits == refsbits ) - { - fundingPubKey = tx.vout[1].scriptPubKey; - fundingtxid = txid; - return(true); - } - } - } - } - return(false); -} - -struct CCcontract_info *Diceinit(CScript &fundingPubKey,uint256 reffundingtxid,struct CCcontract_info *C,char *planstr,uint64_t &txfee,CPubKey &mypk,CPubKey &dicepk,uint64_t &sbits,int64_t &minbet,int64_t &maxbet,int64_t &maxodds,int64_t &timeoutblocks) -{ - struct CCcontract_info *cp; int32_t cmpflag; - cp = CCinit(C,EVAL_DICE); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - dicepk = GetUnspendable(cp,0); - sbits = stringbits(planstr); - if ( reffundingtxid == zeroid ) - cmpflag = 0; - else cmpflag = 1; - if ( DicePlanExists(fundingPubKey,reffundingtxid,cp,sbits,dicepk,minbet,maxbet,maxodds,timeoutblocks) != cmpflag ) - { - fprintf(stderr,"Dice plan (%s) exists.%d vs cmpflag.%d\n",planstr,!cmpflag,cmpflag); - return(0); - } - return(cp); -} - -UniValue DiceInfo(uint256 diceid) -{ - UniValue result(UniValue::VOBJ); CPubKey dicepk; uint256 hashBlock,entropytxid; CTransaction vintx; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t sbits,funding,entropyval; char str[67],numstr[65]; struct CCcontract_info *cp,C; - if ( myGetTransaction(diceid,vintx,hashBlock) == 0 ) - { - fprintf(stderr,"cant find fundingtxid\n"); - ERR_RESULT("cant find fundingtxid"); - return(result); - } - if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) == 0 ) - { - fprintf(stderr,"fundingtxid isnt dice creation txid\n"); - ERR_RESULT("fundingtxid isnt dice creation txid"); - return(result); - } - result.push_back(Pair("result","success")); - result.push_back(Pair("fundingtxid",uint256_str(str,diceid))); - unstringbits(str,sbits); - result.push_back(Pair("name",str)); - result.push_back(Pair("sbits",sbits)); - sprintf(numstr,"%.8f",(double)minbet/COIN); - result.push_back(Pair("minbet",numstr)); - sprintf(numstr,"%.8f",(double)maxbet/COIN); - result.push_back(Pair("maxbet",numstr)); - result.push_back(Pair("maxodds",maxodds)); - result.push_back(Pair("timeoutblocks",timeoutblocks)); - cp = CCinit(&C,EVAL_DICE); - dicepk = GetUnspendable(cp,0); - int32_t entropytxs; - funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,diceid,entropytxs,false); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); - result.push_back(Pair("entropytxs",entropytxs)); - return(result); -} - -UniValue DiceList() -{ - UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction vintx; uint64_t sbits; int64_t minbet,maxbet,maxodds,timeoutblocks; char str[65]; - cp = CCinit(&C,EVAL_DICE); - SetCCtxids(txids,cp->normaladdr,false,cp->evalcode,zeroid,'F'); - for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) - { - txid = *it; - if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) - { - if ( vintx.vout.size() > 0 && DecodeDiceFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,minbet,maxbet,maxodds,timeoutblocks) != 0 ) - { - result.push_back(uint256_str(str,txid)); - } - } - } - return(result); -} - -std::string DiceCreateFunding(uint64_t txfee,char *planstr,int64_t funds,int64_t minbet,int64_t maxbet,int64_t maxodds,int64_t timeoutblocks) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - uint256 zero; CScript fundingPubKey; CPubKey mypk,dicepk; int64_t a,b,c,d; uint64_t sbits; struct CCcontract_info *cp,C; - if ( funds < 0 || minbet < 0 || maxbet < 0 || maxodds < 1 || maxodds > 9999 || timeoutblocks < 0 || timeoutblocks > 1440 ) - { - CCerror = "invalid parameter error"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - if ( funds < 100*COIN ) - { - CCerror = "dice plan needs at least 100 coins"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - memset(&zero,0,sizeof(zero)); - if ( (cp= Diceinit(fundingPubKey,zero,&C,planstr,txfee,mypk,dicepk,sbits,a,b,c,d)) == 0 ) - { - CCerror = "Diceinit error in create funding, is your transaction confirmed?"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - if ( AddNormalinputs(mtx,mypk,funds+3*txfee,60) > 0 ) - { - mtx.vout.push_back(MakeCC1vout(cp->evalcode,funds,dicepk)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(dicepk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceFundingOpRet('F',sbits,minbet,maxbet,maxodds,timeoutblocks))); - } - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); -} - -std::string DiceAddfunding(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t amount) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CScript fundingPubKey,scriptPubKey; uint256 entropy,hentropy; CPubKey mypk,dicepk; uint64_t sbits; struct CCcontract_info *cp,C; int64_t minbet,maxbet,maxodds,timeoutblocks; - if ( amount < 0 ) - { - CCerror = "amount must be positive"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) { - CCerror = "Diceinit error in add funding, is your transaction confirmed?"; - return(""); - } - scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG; - if ( 0 ) - { - uint8_t *ptr0,*ptr1; int32_t i; - for (i=0; i<35; i++) - fprintf(stderr,"%02x",scriptPubKey[i]); - fprintf(stderr," script vs "); - for (i=0; i<35; i++) - fprintf(stderr,"%02x",fundingPubKey[i]); - fprintf(stderr," funding\n"); - } - if ( scriptPubKey == fundingPubKey ) - { - if ( AddNormalinputs2(mtx,amount+2*txfee,60) > 0 ) - { - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,dicepk)); - mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('E',sbits,fundingtxid,hentropy,zeroid))); - } else { - CCerror = "cant find enough inputs"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - } else { - CCerror = "only fund creator can add more funds (entropy)"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - } - return(""); -} - -std::string DiceBet(uint64_t txfee,char *planstr,uint256 fundingtxid,int64_t bet,int32_t odds) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CScript fundingPubKey; CPubKey mypk,dicepk; uint64_t sbits,entropyval,entropyval2; int64_t funding,minbet,maxbet,maxodds,timeoutblocks; uint256 entropytxid,entropytxid2,entropy,hentropy; struct CCcontract_info *cp,C; - if ( bet < 0 ) - { - CCerror = "bet must be positive"; - return(""); - } - if ( odds < 2 || odds > 9999 ) - { - CCerror = "odds must be between 2 and 9999"; - return(""); - } - if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) { - CCerror = "Diceinit error in bet, is your transaction confirmed?"; - return(""); - } - if ( bet < minbet || bet > maxbet || odds > maxodds ) - { - CCerror = strprintf("Dice plan %s illegal bet %.8f: minbet %.8f maxbet %.8f or odds %d vs max.%d\n",planstr,(double)bet/COIN,(double)minbet/COIN,(double)maxbet/COIN,(int32_t)odds,(int32_t)maxodds); - return(""); - } - int32_t entropytxs=0,emptyvar=0; - funding = DicePlanFunds(entropyval,entropytxid,sbits,cp,dicepk,fundingtxid,entropytxs,false); - DicePlanFunds(entropyval2,entropytxid2,sbits,cp,dicepk,fundingtxid,emptyvar,true); - if ( entropyval2 != 0 && entropytxid2 != zeroid ) - { - entropyval = entropyval2; - entropytxid = entropytxid2; - } - if ( funding >= 2*bet*odds+txfee && entropyval != 0 ) - { - if ( entropytxs < 100 ) { - CCerror = "Your dealer is broke, find a new casino."; - return(""); - } - if ( myIsutxo_spentinmempool(ignoretxid,ignorevin,entropytxid,0) != 0 ) - { - CCerror = "entropy txid is spent"; - return(""); - } - mtx.vin.push_back(CTxIn(entropytxid,0,CScript())); - if ( AddNormalinputs(mtx,mypk,bet+2*txfee+odds,60) > 0 ) - { - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,entropyval,dicepk)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bet,dicepk)); - mtx.vout.push_back(CTxOut(txfee+odds,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeDiceOpRet('B',sbits,fundingtxid,entropy,zeroid))); - } else CCerror = "cant find enough normal inputs for %.8f, plan funding %.8f\n"; - } - if ( entropyval == 0 && funding != 0 ) - CCerror = "cant find dice entropy inputs"; - else - CCerror = "cant find dice input"; - return(""); -} - -std::string DiceBetFinish(uint8_t &funcid,uint256 &entropyused,int32_t &entropyvout,int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout,uint256 vin0txid,int32_t vin0vout) -{ - CMutableTransaction savemtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CScript scriptPubKey,fundingPubKey; CTransaction oldbetTx,betTx,entropyTx; uint256 hentropyproof,entropytxid,hashBlock,bettorentropy,entropy,hentropy,oldbettxid; CPubKey mypk,dicepk,fundingpk; struct CCcontract_info *cp,C; int64_t inputs=0,CCchange=0,odds,fundsneeded,minbet,maxbet,maxodds,timeoutblocks; int32_t oldentropyvout,retval=0,iswin=0; uint64_t entropyval,sbits; - entropyused = zeroid; - *resultp = 0; - funcid = 0; - if ( (cp= Diceinit(fundingPubKey,fundingtxid,&C,planstr,txfee,mypk,dicepk,sbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) - { - CCerror = "Diceinit error in finish, is your transaction confirmed?"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - fundingpk = DiceFundingPk(fundingPubKey); - scriptPubKey = CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG; - if ( winlosetimeout != 0 ) // must be dealernode - { - if ( scriptPubKey != fundingPubKey ) - { - //fprintf(stderr,"only dice fund creator can submit winner or loser\n"); - winlosetimeout = 0; - } - } - if ( myGetTransaction(bettxid,betTx,hashBlock) != 0 && myGetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock) != 0 ) - { - entropytxid = betTx.vin[0].prevout.hash; - /*if ( dice_betspent((char *)"DiceBetFinish",bettxid) != 0 ) - { - CCerror = "bettxid already spent"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - }*/ - bettorentropy = DiceGetEntropy(betTx,'B'); - if ( winlosetimeout == 0 || (iswin= DiceIsWinner(hentropyproof,entropyvout,bettxid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) - { - if ( vin0txid == zeroid || vin0vout < 0 ) - { - if ( AddNormalinputs2(mtx,2*txfee,3) == 0 ) // must be a single vin!! - { - CCerror = "no txfee inputs for win/lose"; - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - } - else - { - //fprintf(stderr,"use vin0 %s/%d\n",vin0txid.GetHex().c_str(),vin0vout); - mtx.vin.push_back(CTxIn(vin0txid,vin0vout,CScript())); - } - if ( winlosetimeout != 0 ) // dealernode - { - entropyused = hentropyproof; - if ( vin0vout == -2 ) - retval = -1; - /*if ( iswin == 0 ) - { - retval = -1; - fprintf(stderr,"invalid dicebet %s\n",bettxid.GetHex().c_str()); - } else retval = 0;*/ - if ( retval < 0 || (retval= DiceEntropyUsed(oldbetTx,oldbettxid,oldentropyvout,entropyused,bettxid,betTx,entropyvout)) != 0 ) - { - if ( retval < 0 ) - { - fprintf(stderr,"orphan that reveals entropy, generate refund tx with proofs\n"); - // make sure we dont refund wrong amounts - mtx.vin.push_back(CTxIn(bettxid,0,CScript())); - mtx.vin.push_back(CTxIn(bettxid,1,CScript())); - funcid = 'R'; - mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue,dicepk)); - //mtx.vout.push_back(CTxOut(betTx.vout[0].nValue,fundingPubKey)); - mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); - mtx.vout.push_back(CTxOut(betTx.vout[1].nValue,betTx.vout[2].scriptPubKey)); - *resultp = 1; - return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,entropyused,oldbettxid))); // need to change opreturn to include oldbetTx to allow validation - } - else - { - CCerror = "DiceBetFinish: duplicate betTx"; - *resultp = -2; // demote error to warning - } - //fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - //fprintf(stderr,"set winlosetimeout %d <- %d\n",winlosetimeout,iswin); - if ( (winlosetimeout= iswin) > 0 ) - funcid = 'W'; - else funcid = 'L'; - } - if ( iswin == winlosetimeout ) // dealernode and normal node paths should always get here - { - //fprintf(stderr,"iswin.%d matches\n",iswin); - mtx.vin.push_back(CTxIn(bettxid,0,CScript())); - mtx.vin.push_back(CTxIn(bettxid,1,CScript())); - if ( iswin == 0 && funcid != 'L' && funcid != 'W' ) // normal node path - { - if ( DiceVerifyTimeout(betTx,timeoutblocks) == 0 ) // hasnt timed out yet - { - return(""); - } - else - { - funcid = 'T'; - hentropy = hentropyproof = zeroid; - iswin = 1; - fprintf(stderr,"set timeout win T\n"); - } - } - if ( iswin > 0 && funcid != 0 ) // dealernode 'W' or normal node 'T' path - { - odds = (betTx.vout[2].nValue - txfee); - if ( odds < 1 || odds > maxodds ) - { - CCerror = strprintf("illegal odds.%d vs maxodds.%d\n",(int32_t)odds,(int32_t)maxodds); - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - CCchange = betTx.vout[0].nValue + betTx.vout[1].nValue; - fundsneeded = txfee + (odds+1)*betTx.vout[1].nValue; - savemtx = mtx; - if ( CCchange >= fundsneeded ) - CCchange -= fundsneeded; - else if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,1,sbits,fundingtxid)) >= fundsneeded ) - { - if ( inputs > fundsneeded ) - CCchange += (inputs - fundsneeded); - } - else - { - mtx = savemtx; - if ( (inputs= AddDiceInputs(cp,mtx,dicepk,fundsneeded,60,sbits,fundingtxid)) > 0 ) - { - if ( inputs > fundsneeded ) - CCchange += (inputs - fundsneeded); - } - else - { - CCerror = strprintf("not enough inputs for %.8f\n",(double)fundsneeded/COIN); - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - } - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,dicepk)); - mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); - mtx.vout.push_back(CTxOut((odds+1) * betTx.vout[1].nValue,betTx.vout[2].scriptPubKey)); - } - else // dealernode 'L' path - { - funcid = 'L'; - mtx.vout.push_back(MakeCC1vout(cp->evalcode,betTx.vout[0].nValue + betTx.vout[1].nValue,dicepk)); - mtx.vout.push_back(CTxOut(txfee,fundingPubKey)); - } - //fprintf(stderr,"make tx.%c\n",funcid); - if ( funcid == 'L' || funcid == 'W' ) // dealernode only - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); - else - { - if ( scriptPubKey != betTx.vout[2].scriptPubKey ) - { - CCerror = strprintf("can only finish your own bettxid\n"); - fprintf(stderr,"%s\n", CCerror.c_str() ); - return(""); - } - } - *resultp = 1; - //char str[65],str2[65]; - //fprintf(stderr,"iswin.%d house entropy %s vs bettor %s\n",iswin,uint256_str(str,hentropyproof),uint256_str(str2,bettorentropy)); - return(FinalizeCCTx(0,cp,mtx,fundingpk,txfee,EncodeDiceOpRet(funcid,sbits,fundingtxid,hentropy,hentropyproof))); - } else fprintf(stderr,"iswin.%d does not match.%d\n",iswin,winlosetimeout); - } - else - { - *resultp = -1; - fprintf(stderr,"iswin.%d winlosetimeout.%d\n",iswin,winlosetimeout); - return(""); - } - } - *resultp = -1; - return("couldnt find bettx or entropytx"); -} - -static uint256 dealer0_fundingtxid; -void *dealer0_loop(void *_arg) -{ - char *planstr = (char *)_arg; - CTransaction tx,*entropytxs,entropytx; CPubKey mypk,dicepk; uint64_t entropyval; uint256 hashBlock,entropytxid,txid; int32_t height,lastht,numentropytxs,i,n,m,num; CScript fundingPubKey; struct CCcontract_info *cp,C; char coinaddr[64]; std::string res; int64_t minbet,maxbet,maxodds,timeoutblocks; uint64_t refsbits,txfee = 10000; - if ( (cp= Diceinit(fundingPubKey,dealer0_fundingtxid,&C,planstr,txfee,mypk,dicepk,refsbits,minbet,maxbet,maxodds,timeoutblocks)) == 0 ) - { - fprintf(stderr,"error initializing dealer0_loop\n"); - StartShutdown(); - } - fprintf(stderr,"dealer0 node running\n"); - height = lastht = 0; - entropytxs = (CTransaction *)calloc(sizeof(*entropytxs),DICE_MINUTXOS); - while ( 1 ) - { - while ( HUSH_INSYNC == 0 || (height= HUSH_INSYNC) == lastht ) - { - sleep(3); - } - lastht = height; - fprintf(stderr,"New height.%d\n",height); - DicePlanFunds(entropyval,entropytxid,refsbits,cp,dicepk,dealer0_fundingtxid,numentropytxs,false); - if ( numentropytxs < DICE_MINUTXOS ) - { - n = sqrt(DICE_MINUTXOS - numentropytxs); - //if ( n > 10 ) - // n = 10; - for (i=m=0; i 64 && is_hexstr((char *)res.c_str(),0) > 64 ) - { - if ( DecodeHexTx(tx,res) != 0 ) - { - LOCK(cs_main); - if ( myAddtomempool(tx) != 0 ) - { - fprintf(stderr,"ENTROPY %s: %d of %d, %d\n",tx.GetHash().GetHex().c_str(),i,n,DICE_MINUTXOS - numentropytxs); - RelayTransaction(tx); - entropytxs[m++] = tx; - } else break; - } else break; - } else break; - } - for (i=0; i n.%d\n",num,DICE_MINUTXOS,n); - for (i=0; i > unspentOutputs; - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - if ( vout != 0 ) - continue; - sum += it->second.satoshis; - if ( myGetTransaction(txid,betTx,hashBlock) != 0 && betTx.vout.size() >= 4 && betTx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( DecodeDiceOpRet(txid,betTx.vout[betTx.vout.size()-1].scriptPubKey,sbits,fundingtxid,hash,proof) == 'B' && sbits == refsbits ) - { - if ( myGetTransaction(betTx.vin[0].prevout.hash,entropyTx,hashBlock) != 0 ) - { - flag = HUSH_DEALERNODE != 0; - if ( HUSH_DEALERNODE != 0 && scriptPubKey == fundingPubKey ) - { - bettorentropy = DiceGetEntropy(betTx,'B'); - if ( (iswin= DiceIsWinner(hentropyproof,entropyvout,txid,betTx,entropyTx,bettorentropy,sbits,minbet,maxbet,maxodds,timeoutblocks,fundingtxid)) != 0 ) - { - if ( iswin > 0 ) - win++; - else if ( iswin < 0 ) - loss++; - n++; - DiceQueue(iswin,sbits,fundingtxid,txid,betTx,entropyvout); - } - } - if ( scriptPubKey != fundingPubKey ) - { - fprintf(stderr,"serialized bettxid %d: iswin.%d W.%d L.%d %s/v%d (%c %.8f) %.8f\n",n,iswin,win,loss,txid.GetHex().c_str(),vout,funcid,(double)it->second.satoshis/COIN,(double)sum/COIN); - res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,txid,scriptPubKey == fundingPubKey,zeroid,-1); - if ( result > 0 ) - { - mySenddicetransaction(res,entropyused,entropyvout,txid,betTx,funcid,0); - n++; - if ( n > 10 ) - break; - } - } - } else fprintf(stderr,"bettxid.%s cant find entropyTx.%s\n",txid.GetHex().c_str(),betTx.vin[0].prevout.hash.GetHex().c_str()); - } - } - } - if ( didinit == 0 && HUSH_DEALERNODE == 0 && scriptPubKey == fundingPubKey ) - { - strcpy(_planstr,planstr); - dealer0_fundingtxid = fundingtxid; - if ( pthread_create((pthread_t *)malloc(sizeof(pthread_t)),NULL,dealer0_loop,_planstr) == 0 ) - didinit = 1; - } - return(n); - } - else - { - char str[65]; - if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 ) - { - //fprintf(stderr,"bettx is spent\n"); - if ( myGetTransaction(bettxid,betTx,hashBlock) != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() > 2 ) - { - //fprintf(stderr,"found spenttxid %s\n",uint256_str(str,spenttxid)); - if ( betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey ) - return(0.); - else return((double)spenttx.vout[2].nValue/COIN); - } - CCerror = "couldnt find bettx or spenttx %s\n",uint256_str(str,spenttxid); - return(-1.); - } - else if ( scriptPubKey == fundingPubKey ) - res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,bettxid,1,zeroid,-1); - else res = DiceBetFinish(funcid,entropyused,entropyvout,&result,txfee,planstr,fundingtxid,bettxid,0,zeroid,-1); - if ( result > 0 ) - { - mySenddicetransaction(res,entropyused,entropyvout,bettxid,betTx,funcid,0); - sleep(1); - if ( (vout= myIsutxo_spent(spenttxid,bettxid,1)) >= 0 ) - { - if ( myGetTransaction(txid,betTx,hashBlock) != 0 && myGetTransaction(spenttxid,spenttx,hashBlock) != 0 && spenttx.vout.size() >= 2 ) - { - if ( funcid == 'L' )//betTx.vout[1].scriptPubKey.IsPayToCryptoCondition() == 0 || betTx.vout[2].scriptPubKey.IsPayToCryptoCondition() != 0 || spenttx.vout[2].scriptPubKey != betTx.vout[2].scriptPubKey ) - //if ( spenttx.vout[2].scriptPubKey == fundingPubKey || ((uint8_t *)spenttx.vout[2].scriptPubKey.data())[0] == 0x6a ) - return(0.); - else return((double)spenttx.vout[2].nValue/COIN); - } else return(0.); - } - CCerror = "didnt find dicefinish tx"; - } else CCerror = res; - return(-1.); - } - return(0.); -} diff --git a/src/cc/lotto.cpp b/src/cc/lotto.cpp deleted file mode 100644 index c7621178a..000000000 --- a/src/cc/lotto.cpp +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright (c) 2016-2023 The Hush developers -// Distributed under the GPLv3 software license, see the accompanying -// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html -/****************************************************************************** - * Copyright © 2014-2019 The SuperNET Developers. * - * * - * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * - * the top-level directory of this distribution for the individual copyright * - * holder information and the developer policies on copyright and licensing. * - * * - * Unless otherwise agreed in a custom licensing agreement, no part of the * - * SuperNET software, including this file may be copied, modified, propagated * - * or distributed except according to the terms contained in the LICENSE file * - * * - * Removal or modification of this copyright notice is prohibited. * - * * - ******************************************************************************/ - -#include "CClotto.h" -#include "../txmempool.h" - -/* - A blockchain lotto has the problem of generating the deterministic random numbers needed to get a winner in a way that doesnt allow cheating. If we save the entropy for later publishing and display the hash of the entropy, it is true that the players wont know what the entropy value is, however the creator of the lotto funds will be able to know and simply create a winning ticket when the jackpot is large enough. - - We also need to avoid chain reorgs from disclosing the entropy and then allowing people to submit a winning ticket calculated based on the disclosed entropy (see attack vector in dice.cpp) - - As usual it needs to be provably fair and random - - The solution is for everybody to post the hash of their entropy when purchasing tickets. Then at the time of the drawing, nodes would post their entropy over an N block period to avoid censorship attack. After the N block period, then we have valid entropy that we know was locked in prior to the start of the N blocks and that nobody would have been able to know ahead of time the final entropy value. - - As long as one node submits a high entropy value, then just by combining all the submissions together, we get the drawing's entropy value. Given that, the usual process can determine if there was a winner at the specified odds. In fact, all the nodes are able to determine exactly how many winners there were and whether to validate 1/w payouts to the w winners or rollover the jackpot to the next drawing. - - To remove the need for an autopayout, the winning node(s) would need to submit a 1/w payout tx, this would be able to be done at any time and the winner does not have to have submitted proof of hentropy. In order to prevent a player from opportunistically withholding their entropy, the lotto creator will post the original proof of hentropy after the N block player submission period. This masks to all the players the final value of entropy. - - Attack vector: the lotto creator can have many player tickets in reserve all with their entropy ready to submit, but based on the actual submissions, find the one which gives him the best outcome. since all the player submissions will be known via mempool, along with the original hentropy. However the lotto creator would have to mine the final block in order to know the order of the player tickets. - - Thinking about this evil miner attack, it seems pretty bad, so a totally new approach is needed. Preferably with a simple enough protocol. Let us remove any special knowledge by the lotto creator, so like the faucet, it seems just that there is a single lotto for a chain. - - >>>>>>>>>>>> second iteration - - What we need is something that gives each ticket an equal chance at the jackpot, without allowing miner or relayer to gain an advantage. ultimately the jackpot payout tx needs to be confirmed, so there needs to be some number of blocks to make a claim to avoid censorship attack. If onchain entropy is needed, then it should be reduced to 1 bit per block to reduce the grinding that is possible. This does mean a block miner for the last bit of entropy can double their chances at winning, but the alternative is to have an external source of entropy, which creates its own set of issues like what prevents the nodes getting the external entropy from cheating? - - Conveniently the lotto mechanics are similar to a PoS staking, so it can be based on everybody trying to stake a single lotto jackpot. - - The calculation would need to be based on the payout address and utxosize, so relayers cant intercept it to steal the jackpot. - - each jackpot would effectively restart the lotto - - the funds from new lotto tickets can be spent by the jackpot, but those tickets can still win the new jackpot - - each set of tickets (utxo) would become eligible to claim the jackpot after some time is elapsed so the entropy for that utxo can be obtained. [6 bits * 32 + 1 bit * 16] 48 blocks - -It is possible to have a jackpot but miss out on it due to not claiming it. To minimize the effect from this, each ticket would have one chance to win, which can be calculated and a jackpot claim submitted just once. - - in order to randomize the timing of claim, a txid PoW similar to faucetget will maximize the chance of only a single jackpot txid that can propagate throughout the mempools, which will prevent the second one broadcast. Granted the mining node can override this if they also have a winning ticket, but assuming the PoS lottery makes it unlikely for two winners in a single block, this is not a big issue. - - In order to adapt the difficulty of winning the lotto, but not requiring recalculating all past tickets, as new lotto tickets are sold without a jackpot, it needs to become easier to win. Basically as the lotto jackpot gets bigger and bigger, it keeps getting easier to win! This convergence will avoid having unwinnable jackpots. - - rpc calls - lottoinfo - lottotickets - lottostatus - lottowinner tickethash ticketid - -*/ - -// start of consensus code - -int64_t IsLottovout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v) -{ - char destaddr[64]; - if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 ) - return(tx.vout[v].nValue); - } - return(0); -} - -bool LottoExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) -{ - static uint256 zerohash; - CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) - { - //fprintf(stderr,"vini.%d check mempool\n",i); - if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) - return eval->Invalid("cant find vinTx"); - else - { - //fprintf(stderr,"vini.%d check hash and vout\n",i); - if ( hashBlock == zerohash ) - return eval->Invalid("cant Lotto from mempool"); - if ( (assetoshis= IsLottovout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) - inputs += assetoshis; - } - } - } - for (i=0; iInvalid("mismatched inputs != outputs + txfee"); - } - else return(true); -} - -bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) -{ - int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval; - return eval->Invalid("no validation yet"); - numvins = tx.vin.size(); - numvouts = tx.vout.size(); - preventCCvins = preventCCvouts = -1; - if ( numvouts < 1 ) - return eval->Invalid("no vouts"); - else - { - //fprintf(stderr,"check vins\n"); - for (i=0; iInvalid("illegal normal vini"); - } - } - //fprintf(stderr,"check amounts\n"); - if ( LottoExactAmounts(cp,eval,tx,1,10000) == false ) - { - fprintf(stderr,"Lottoget invalid amount\n"); - return false; - } - else - { - preventCCvouts = 1; - if ( IsLottovout(cp,tx,0) != 0 ) - { - preventCCvouts++; - i = 1; - } else i = 0; - if ( tx.vout[i].nValue != COIN ) - return eval->Invalid("invalid Lotto output"); - retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); - if ( retval != 0 ) - fprintf(stderr,"Lottoget validated\n"); - else fprintf(stderr,"Lottoget invalid\n"); - return(retval); - } - } -} -// end of consensus code - -// helper functions for rpc calls in rpcwallet.cpp - -int64_t AddLottoInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) -{ - // add threshold check - char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t n = 0; - std::vector > unspentOutputs; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - // prevent dup - if ( it->second.satoshis < COIN ) - continue; - if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) - { - if ( (nValue= IsLottovout(cp,vintx,(int32_t)it->first.index)) > 0 ) - { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,(int32_t)it->first.index,CScript())); - nValue = it->second.satoshis; - totalinputs += nValue; - n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) - break; - } - } - } - return(totalinputs); -} - -uint8_t DecodeLottoFundingOpRet(const CScript &scriptPubKey,uint64_t &sbits,int32_t ticketsize,int32_t odds,int32_t firstheight,int32_t period,uint256 hentropy) -{ - std::vector vopret; uint8_t *script,e,f; - GetOpReturnData(scriptPubKey, vopret); - script = (uint8_t *)vopret.data(); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> sbits; ss >> ticketsize; ss >> odds; ss >> firstheight; ss >> period; ss >> hentropy) != 0 ) - { - if ( e == EVAL_LOTTO && f == 'F' ) - return(f); - } - return(0); -} - -int64_t LottoPlanFunds(uint64_t refsbits,struct CCcontract_info *cp,CPubKey pk,uint256 reffundingtxid) -{ - char coinaddr[64]; uint64_t sbits; int64_t nValue,lockedfunds; uint256 txid,hashBlock,fundingtxid; CTransaction tx; int32_t vout; uint8_t funcid; - std::vector > unspentOutputs; - lockedfunds = 0; - GetCCaddress(cp,coinaddr,pk); - SetCCunspents(unspentOutputs,coinaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) - { - txid = it->first.txhash; - vout = (int32_t)it->first.index; - if ( myGetTransaction(txid,tx,hashBlock) != 0 && tx.vout[vout].scriptPubKey.IsPayToCryptoCondition() != 0 ) - { - // need to implement this! if ( (funcid= DecodeLottoOpRet(txid,tx.vout[tx.vout.size()-1].scriptPubKey,sbits,fundingtxid)) == 'F' || funcid == 'T' ) - { - if ( refsbits == sbits && (funcid == 'F' && reffundingtxid == txid) || reffundingtxid == fundingtxid ) - { - if ( (nValue= IsLottovout(cp,tx,vout)) > 0 ) - lockedfunds += nValue; - else fprintf(stderr,"refsbits.%llx sbits.%llx nValue %.8f\n",(long long)refsbits,(long long)sbits,(double)nValue/COIN); - } //else fprintf(stderr,"else case\n"); - } //else fprintf(stderr,"funcid.%d %c skipped %.8f\n",funcid,funcid,(double)tx.vout[vout].nValue/COIN); - } - } - return(lockedfunds); -} - -UniValue LottoInfo(uint256 lottoid) -{ - UniValue result(UniValue::VOBJ); uint256 hashBlock,hentropy; CTransaction vintx; uint64_t lockedfunds,sbits; int32_t ticketsize,odds,firstheight,period; CPubKey lottopk; struct CCcontract_info *cp,C; char str[67],numstr[65]; - if ( myGetTransaction(lottoid,vintx,hashBlock) == 0 ) - { - fprintf(stderr,"cant find lottoid\n"); - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find lottoid")); - return(result); - } - if ( vintx.vout.size() > 0 && DecodeLottoFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,ticketsize,odds,firstheight,period,hentropy) == 0 ) - { - fprintf(stderr,"lottoid isnt lotto creation txid\n"); - result.push_back(Pair("result","error")); - result.push_back(Pair("error","lottoid isnt lotto creation txid")); - return(result); - } - result.push_back(Pair("result","success")); - result.push_back(Pair("lottoid",uint256_str(str,lottoid))); - unstringbits(str,sbits); - result.push_back(Pair("name",str)); - result.push_back(Pair("sbits",sbits)); - result.push_back(Pair("ticketsize",ticketsize)); - result.push_back(Pair("odds",odds)); - cp = CCinit(&C,EVAL_LOTTO); - lottopk = GetUnspendable(cp,0); - lockedfunds = LottoPlanFunds(sbits,cp,lottopk,lottoid); - sprintf(numstr,"%.8f",(double)lockedfunds/COIN); - result.push_back(Pair("jackpot",numstr)); - return(result); -} - -UniValue LottoList() -{ - UniValue result(UniValue::VARR); std::vector txids; struct CCcontract_info *cp,C; uint256 txid,hashBlock,hentropy; CTransaction vintx; uint64_t sbits; int32_t ticketsize,odds,firstheight,period; char str[65]; - cp = CCinit(&C,EVAL_LOTTO); - SetCCtxids(txids,cp->normaladdr,true,cp->evalcode,zeroid,'F'); - for (std::vector::const_iterator it=txids.begin(); it!=txids.end(); it++) - { - txid = *it; - if ( myGetTransaction(txid,vintx,hashBlock) != 0 ) - { - if ( vintx.vout.size() > 0 && DecodeLottoFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,sbits,ticketsize,odds,firstheight,period,hentropy) == 'F' ) - { - result.push_back(uint256_str(str,txid)); - } - } - } - return(result); -} - -std::string LottoCreate(uint64_t txfee,char *planstr,int64_t funding,int32_t ticketsize,int32_t odds,int32_t firstheight,int32_t period) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - uint256 entropy,hentropy; CPubKey mypk,lottopk; uint64_t sbits; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_LOTTO); - if ( txfee == 0 ) - txfee = 10000; - lottopk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - sbits = stringbits(planstr); - if ( AddNormalinputs(mtx,mypk,funding+txfee,60) > 0 ) - { - hentropy = DiceHashEntropy(entropy,mtx.vin[0].prevout.hash,mtx.vin[0].prevout.n,1); - mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,funding,lottopk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_LOTTO << (uint8_t)'F' << sbits << ticketsize << odds << firstheight << period << hentropy))); - } - return(""); -} - -std::string LottoTicket(uint64_t txfee,uint256 lottoid,int64_t numtickets) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CPubKey mypk,lottopk; CScript opret; int64_t inputs,CCchange=0,nValue=COIN; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_LOTTO); - if ( txfee == 0 ) - txfee = 10000; - lottopk = GetUnspendable(cp,0); - mypk = pubkey2pk(Mypubkey()); - if ( (inputs= AddLottoInputs(cp,mtx,lottopk,nValue+txfee,60)) > 0 ) - { - if ( inputs > nValue ) - CCchange = (inputs - nValue - txfee); - if ( CCchange != 0 ) - mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,CCchange,lottopk)); - mtx.vout.push_back(CTxOut(nValue,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - return(FinalizeCCTx(-1LL,cp,mtx,mypk,txfee,opret)); - } else fprintf(stderr,"cant find Lotto inputs\n"); - return(""); -} - -std::string LottoWinner(uint64_t txfee) -{ - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), hush_nextheight()); - CPubKey mypk,lottopk; int64_t winnings = 0; CScript opret; struct CCcontract_info *cp,C; - cp = CCinit(&C,EVAL_LOTTO); - if ( txfee == 0 ) - txfee = 10000; - mypk = pubkey2pk(Mypubkey()); - lottopk = GetUnspendable(cp,0); - if ( AddNormalinputs(mtx,mypk,txfee,64) > 0 ) - { - mtx.vout.push_back(MakeCC1vout(EVAL_LOTTO,winnings,lottopk)); - return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); - } - return(""); -} - - diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index d764f392d..1fd035660 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -389,9 +389,6 @@ static const CRPCCommand vRPCCommands[] = // auction { "auction", "auctionaddress", &auctionaddress, true }, - // lotto - { "lotto", "lottoaddress", &lottoaddress, true }, - // fsm { "FSM", "FSMaddress", &FSMaddress, true }, { "FSM", "FSMcreate", &FSMcreate, true }, @@ -439,15 +436,6 @@ static const CRPCCommand vRPCCommands[] = { "heir", "heirinfo", &heirinfo, true }, { "heir", "heirlist", &heirlist, true }, - // Channels - { "channels", "channelsaddress", &channelsaddress, true }, - { "channels", "channelslist", &channelslist, true }, - { "channels", "channelsinfo", &channelsinfo, true }, - { "channels", "channelsopen", &channelsopen, true }, - { "channels", "channelspayment", &channelspayment, true }, - { "channels", "channelsclose", &channelsclose, true }, - { "channels", "channelsrefund", &channelsrefund, true }, - // Oracles { "oracles", "oraclesaddress", &oraclesaddress, true }, { "oracles", "oracleslist", &oracleslist, true }, @@ -476,16 +464,6 @@ static const CRPCCommand vRPCCommands[] = { "CClib", "cclibinfo", &cclibinfo, true }, { "CClib", "cclib", &cclib, true }, - // dice - { "dice", "dicelist", &dicelist, true }, - { "dice", "diceinfo", &diceinfo, true }, - { "dice", "dicefund", &dicefund, true }, - { "dice", "diceaddfunds", &diceaddfunds, true }, - { "dice", "dicebet", &dicebet, true }, - { "dice", "dicefinish", &dicefinish, true }, - { "dice", "dicestatus", &dicestatus, true }, - { "dice", "diceaddress", &diceaddress, true }, - // tokens & assets { "tokens", "assetsaddress", &assetsaddress, true }, { "tokens", "tokeninfo", &tokeninfo, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index 8f90627fe..d805fab63 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -303,15 +303,6 @@ extern UniValue rewardscreatefunding(const UniValue& params, bool fHelp, const C extern UniValue rewardsaddfunding(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue rewardslock(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue rewardsunlock(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue diceaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicefund(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicelist(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue diceinfo(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue diceaddfunds(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicebet(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicefinish(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue dicestatus(const UniValue& params, bool fHelp, const CPubKey& mypk); -extern UniValue lottoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue FSMaddress(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue FSMcreate(const UniValue& params, bool fHelp, const CPubKey& mypk); extern UniValue FSMlist(const UniValue& params, bool fHelp, const CPubKey& mypk); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 494c4565c..d725fa7b7 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -6092,11 +6092,8 @@ int32_t hush_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33, void *pT #include "../cc/CCfaucet.h" #include "../cc/CCassets.h" #include "../cc/CCrewards.h" -#include "../cc/CCdice.h" #include "../cc/CCfsm.h" #include "../cc/CCauction.h" -#include "../cc/CClotto.h" -#include "../cc/CCchannels.h" #include "../cc/CCOracles.h" #include "../cc/CCPrices.h" #include "../cc/CCHeir.h" @@ -6260,19 +6257,6 @@ UniValue setpubkey(const UniValue& params, bool fHelp, const CPubKey& mypk) return result; } -UniValue channelsaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::vector pubkey; - - cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() != 1 ) - throw runtime_error("channelsaddress pubkey\n"); - if ( ensure_CCrequirements(cp->evalcode) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - pubkey = ParseHex(params[0].get_str().c_str()); - return(CCaddress(cp,(char *)"Channels",pubkey)); -} - UniValue cclibaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp,C; std::vector pubkey; uint8_t evalcode = EVAL_FIRSTUSER; @@ -6497,19 +6481,6 @@ UniValue heiraddress(const UniValue& params, bool fHelp, const CPubKey& mypk) return(CCaddress(cp,(char *)"Heir",pubkey)); } -UniValue lottoaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - struct CCcontract_info *cp,C; std::vector pubkey; - cp = CCinit(&C,EVAL_LOTTO); - if ( fHelp || params.size() > 1 ) - throw runtime_error("lottoaddress [pubkey]\n"); - if ( ensure_CCrequirements(cp->evalcode) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - if ( params.size() == 1 ) - pubkey = ParseHex(params[0].get_str().c_str()); - return(CCaddress(cp,(char *)"Lotto",pubkey)); -} - UniValue FSMaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp,C; std::vector pubkey; @@ -6536,19 +6507,6 @@ UniValue auctionaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) return(CCaddress(cp,(char *)"Auction",pubkey)); } -UniValue diceaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - struct CCcontract_info *cp,C; std::vector pubkey; - cp = CCinit(&C,EVAL_DICE); - if ( fHelp || params.size() > 1 ) - throw runtime_error("diceaddress [pubkey]\n"); - if ( ensure_CCrequirements(cp->evalcode) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - if ( params.size() == 1 ) - pubkey = ParseHex(params[0].get_str().c_str()); - return(CCaddress(cp,(char *)"Dice",pubkey)); -} - UniValue faucetaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) { struct CCcontract_info *cp,C; std::vector pubkey; @@ -6603,118 +6561,6 @@ UniValue tokenaddress(const UniValue& params, bool fHelp, const CPubKey& mypk) return(CCaddress(cp,(char *)"Tokens", pubkey)); } -UniValue channelslist(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if ( fHelp || params.size() > 0 ) - throw runtime_error("channelslist\n"); - if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - return(ChannelsList(mypk)); -} - -UniValue channelsinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint256 opentxid; - if ( fHelp || params.size() > 1 ) - throw runtime_error("channelsinfo [opentxid]\n"); - if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - opentxid=zeroid; - if (params.size() > 0 && !params[0].isNull() && !params[0].get_str().empty()) - opentxid = Parseuint256((char *)params[0].get_str().c_str()); - return(ChannelsInfo(mypk,opentxid)); -} - -UniValue channelsopen(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); int32_t numpayments; int64_t payment; std::vector destpub; struct CCcontract_info *cp,C; - uint256 tokenid=zeroid; - - cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() < 3 || params.size() > 4) - throw runtime_error("channelsopen destpubkey numpayments payment [tokenid]\n"); - if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - destpub = ParseHex(params[0].get_str().c_str()); - numpayments = atoi(params[1].get_str().c_str()); - payment = atol(params[2].get_str().c_str()); - if (params.size()==4) - { - tokenid=Parseuint256((char *)params[3].get_str().c_str()); - } - result = ChannelOpen(mypk,0,pubkey2pk(destpub),numpayments,payment,tokenid); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { - result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); - return(result); -} - -UniValue channelspayment(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 opentxid,secret=zeroid; int32_t n; int64_t amount; - cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() < 2 || params.size() >3 ) - throw runtime_error("channelspayment opentxid amount [secret]\n"); - if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - opentxid = Parseuint256((char *)params[0].get_str().c_str()); - amount = atoi((char *)params[1].get_str().c_str()); - if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) - { - secret = Parseuint256((char *)params[2].get_str().c_str()); - } - result = ChannelPayment(mypk,0,opentxid,amount,secret); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { - result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); - return(result); -} - -UniValue channelsclose(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 opentxid; - cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() != 1 ) - throw runtime_error("channelsclose opentxid\n"); - if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - opentxid = Parseuint256((char *)params[0].get_str().c_str()); - result = ChannelClose(mypk,0,opentxid); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { - result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); - return(result); -} - -UniValue channelsrefund(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; uint256 opentxid,closetxid; - cp = CCinit(&C,EVAL_CHANNELS); - if ( fHelp || params.size() != 2 ) - throw runtime_error("channelsrefund opentxid closetxid\n"); - if ( ensure_CCrequirements(EVAL_CHANNELS) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - Lock2NSPV(mypk); - opentxid = Parseuint256((char *)params[0].get_str().c_str()); - closetxid = Parseuint256((char *)params[1].get_str().c_str()); - result = ChannelRefund(mypk,0,opentxid,closetxid); - if ( result[JSON_HEXTX].getValStr().size() > 0 ) - { - result.push_back(Pair("result", "success")); - } - Unlock2NSPV(mypk); - return(result); -} - UniValue rewardscreatefunding(const UniValue& params, bool fHelp, const CPubKey& mypk) { UniValue result(UniValue::VOBJ); char *name; int64_t funds,APR,minseconds,maxseconds,mindeposit; std::string hex; @@ -7179,204 +7025,6 @@ UniValue faucetget(const UniValue& params, bool fHelp, const CPubKey& mypk) return(result); } - -UniValue dicefund(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); int64_t funds,minbet,maxbet,maxodds,timeoutblocks; std::string hex; char *name; - if ( fHelp || params.size() != 6 ) - throw runtime_error("dicefund name funds minbet maxbet maxodds timeoutblocks\n"); - if ( ensure_CCrequirements(EVAL_DICE) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - name = (char *)params[0].get_str().c_str(); - funds = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999; - minbet = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - maxbet = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; - maxodds = atol(params[4].get_str().c_str()); - timeoutblocks = atol(params[5].get_str().c_str()); - - if (!VALID_PLAN_NAME(name)) { - ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); - return(result); - } - - hex = DiceCreateFunding(0,name,funds,minbet,maxbet,maxodds,timeoutblocks); - if (CCerror != "") { - ERR_RESULT(CCerror); - } else if ( hex.size() > 0 ) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else { - ERR_RESULT( "couldnt create dice funding transaction"); - } - return(result); -} - -UniValue diceaddfunds(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; int64_t amount; std::string hex; - if ( fHelp || params.size() != 3 ) - throw runtime_error("diceaddfunds name fundingtxid amount\n"); - if ( ensure_CCrequirements(EVAL_DICE) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - name = (char *)params[0].get_str().c_str(); - fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - if (!VALID_PLAN_NAME(name)) { - ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); - return(result); - } - if ( amount > 0 ) { - hex = DiceAddfunding(0,name,fundingtxid,amount); - if (CCerror != "") { - ERR_RESULT(CCerror); - } else if ( hex.size() > 0 ) { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } else ERR_RESULT("couldnt create dice addfunding transaction"); - } else ERR_RESULT("amount must be positive"); - return(result); -} - -UniValue dicebet(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); std::string hex,error; uint256 fundingtxid; int64_t amount,odds; char *name; - if ( fHelp || params.size() != 4 ) - throw runtime_error("dicebet name fundingtxid amount odds\n"); - if ( ensure_CCrequirements(EVAL_DICE) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - name = (char *)params[0].get_str().c_str(); - fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); - amount = atof(params[2].get_str().c_str()) * COIN + 0.00000000499999; - odds = atol(params[3].get_str().c_str()); - - if (!VALID_PLAN_NAME(name)) { - ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); - return(result); - } - if (amount > 0 && odds > 0) { - hex = DiceBet(0,name,fundingtxid,amount,odds); - RETURN_IF_ERROR(CCerror); - if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - } - } else { - ERR_RESULT("amount and odds must be positive"); - } - return(result); -} - -UniValue dicefinish(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); uint8_t funcid; char *name; uint256 entropyused,fundingtxid,bettxid; std::string hex; int32_t r,entropyvout; - if ( fHelp || params.size() != 3 ) - throw runtime_error("dicefinish name fundingtxid bettxid\n"); - if ( ensure_CCrequirements(EVAL_DICE) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - name = (char *)params[0].get_str().c_str(); - if (!VALID_PLAN_NAME(name)) { - ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); - return(result); - } - fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - hex = DiceBetFinish(funcid,entropyused,entropyvout,&r,0,name,fundingtxid,bettxid,1,zeroid,-1); - if ( CCerror != "" ) - { - ERR_RESULT(CCerror); - } else if ( hex.size() > 0 ) - { - result.push_back(Pair("result", "success")); - result.push_back(Pair("hex", hex)); - if ( funcid != 0 ) - { - char funcidstr[2]; - funcidstr[0] = funcid; - funcidstr[1] = 0; - result.push_back(Pair("funcid", funcidstr)); - } - } else ERR_RESULT( "couldnt create dicefinish transaction"); - return(result); -} - -UniValue dicestatus(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid,bettxid; std::string status,error; double winnings; - if ( fHelp || (params.size() != 2 && params.size() != 3) ) - throw runtime_error("dicestatus name fundingtxid bettxid\n"); - if ( ensure_CCrequirements(EVAL_DICE) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - const CKeyStore& keystore = *pwalletMain; - LOCK2(cs_main, pwalletMain->cs_wallet); - name = (char *)params[0].get_str().c_str(); - if (!VALID_PLAN_NAME(name)) { - ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); - return(result); - } - fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); - memset(&bettxid,0,sizeof(bettxid)); - if ( params.size() == 3 ) - bettxid = Parseuint256((char *)params[2].get_str().c_str()); - winnings = DiceStatus(0,name,fundingtxid,bettxid); - RETURN_IF_ERROR(CCerror); - - result.push_back(Pair("result", "success")); - if ( winnings >= 0. ) - { - if ( winnings > 0. ) - { - if ( params.size() == 3 ) - { - int64_t val; - val = winnings * COIN + 0.00000000499999; - result.push_back(Pair("status", "win")); - result.push_back(Pair("won", ValueFromAmount(val))); - } - else - { - result.push_back(Pair("status", "finalized")); - result.push_back(Pair("n", (int64_t)winnings)); - } - } - else - { - if ( params.size() == 3 ) - result.push_back(Pair("status", "loss")); - else result.push_back(Pair("status", "no pending bets")); - } - } else result.push_back(Pair("status", "bet still pending")); - return(result); -} - -UniValue dicelist(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - if ( fHelp || params.size() > 0 ) - throw runtime_error("dicelist\n"); - if ( ensure_CCrequirements(EVAL_DICE) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - return(DiceList()); -} - -UniValue diceinfo(const UniValue& params, bool fHelp, const CPubKey& mypk) -{ - uint256 fundingtxid; - if ( fHelp || params.size() != 1 ) - throw runtime_error("diceinfo fundingtxid\n"); - if ( ensure_CCrequirements(EVAL_DICE) < 0 ) - throw runtime_error(CC_REQUIREMENTS_MSG); - fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - return(DiceInfo(fundingtxid)); -} - UniValue tokenlist(const UniValue& params, bool fHelp, const CPubKey& mypk) { uint256 tokenid;