Browse Source

remove dice, channels, lotto

duke
Duke 4 months ago
parent
commit
657bb1e25e
  1. 3
      src/Makefile.am
  2. 35
      src/cc/CCchannels.h
  3. 15
      src/cc/CCcustom.cpp
  4. 38
      src/cc/CCdice.h
  5. 34
      src/cc/CClotto.h
  6. 807
      src/cc/channels.cpp
  7. 1871
      src/cc/dice.cpp
  8. 344
      src/cc/lotto.cpp
  9. 22
      src/rpc/server.cpp
  10. 9
      src/rpc/server.h
  11. 352
      src/wallet/rpcwallet.cpp

3
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 \

35
src/cc/CCchannels.h

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

15
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);

38
src/cc/CCdice.h

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

34
src/cc/CClotto.h

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

807
src/cc/channels.cpp

@ -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<CPubKey> 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<std::pair<uint8_t, vscript_t>> oprets;
std::vector<uint8_t> vopret,vOpretExtra; uint8_t *script,e,f,tokenevalcode;
std::vector<CPubKey> 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::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<CTransaction> tmp_txs;
myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P');
for (std::vector<CTransaction>::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; i<numpayments; i++)
{
vcalc_sha256(0,hashdest,hash,32);
memcpy(hash,hashdest,32);
}
endiancpy((uint8_t *)&hashchain,hashdest,32);
if (tokenid!=zeroid) mtx.vout.push_back(MakeTokensCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub));
else mtx.vout.push_back(MakeCC1of2vout(EVAL_CHANNELS,funds,mypk,destpub));
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,mypk));
mtx.vout.push_back(MakeCC1vout(EVAL_CHANNELS,CC_MARKER_VALUE,destpub));
if (tokenid!=zeroid && tokens>funds) mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS,tokens-funds,mypk));
return(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<payment)
CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid amount, not a magnitude of payment size");
else if (mypk == destpub && secret==zeroid) CCERR_RESULT("channelscc",CCLOG_INFO, stream << "invalid secret, secret is necessary when making payment from destination");
}
else
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 (AddNormalinputs(mtx,mypk,txfee+CC_MARKER_VALUE,3,pk.IsValid()) > 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<uint256> 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<uint256>::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<uint256> txids; std::vector<CTransaction> 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<uint256>::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<CTransaction> tmp_txs;
myGet_mempool_txs(tmp_txs,EVAL_CHANNELS,'P');
for (std::vector<CTransaction>::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<CTransaction>::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);
}

1871
src/cc/dice.cpp

File diff suppressed because it is too large

344
src/cc/lotto.cpp

@ -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 <numtickets>
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; i<numvins; i++)
{
//fprintf(stderr,"vini.%d\n",i);
if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
{
//fprintf(stderr,"vini.%d check mempool\n",i);
if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
return eval->Invalid("cant find vinTx");
else
{
//fprintf(stderr,"vini.%d check hash and vout\n",i);
if ( hashBlock == zerohash )
return eval->Invalid("cant Lotto from mempool");
if ( (assetoshis= IsLottovout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
inputs += assetoshis;
}
}
}
for (i=0; i<numvouts; i++)
{
//fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
if ( (assetoshis= IsLottovout(cp,tx,i)) != 0 )
outputs += assetoshis;
}
if ( inputs != outputs+txfee )
{
fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
return eval->Invalid("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; i<numvins; i++)
{
if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
{
fprintf(stderr,"Lottoget invalid vini\n");
return eval->Invalid("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<uint8_t> origpubkey; CTransaction vintx; int32_t n = 0;
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;
// 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<uint8_t> 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<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
lockedfunds = 0;
GetCCaddress(cp,coinaddr,pk);
SetCCunspents(unspentOutputs,coinaddr,true);
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
{
txid = it->first.txhash;
vout = (int32_t)it->first.index;
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<uint256> 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<uint256>::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("");
}

22
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 },

9
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);

352
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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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<unsigned char> 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;

Loading…
Cancel
Save