diff --git a/src/Makefile.am b/src/Makefile.am index fd02d4732..e40cd43db 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -289,6 +289,8 @@ libbitcoin_server_a_SOURCES = \ cc/CCcustom.cpp \ cc/CCtx.cpp \ cc/CCutils.cpp \ + cc/CoinbaseGuard.cpp \ + cc/CoinbaseGuard.h \ cc/assets.cpp \ cc/faucet.cpp \ cc/rewards.cpp \ diff --git a/src/cc/CCGateways.h b/src/cc/CCGateways.h index 1d7bef9d5..d83263af2 100644 --- a/src/cc/CCGateways.h +++ b/src/cc/CCGateways.h @@ -20,7 +20,7 @@ #include "CCinclude.h" #include "../merkleblock.h" -bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector pubkeys); std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vectorproof,CPubKey destpub,int64_t amount); std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount); diff --git a/src/cc/CCMofN.h b/src/cc/CCMofN.h index 170200d77..9b6c56b6f 100644 --- a/src/cc/CCMofN.h +++ b/src/cc/CCMofN.h @@ -21,7 +21,7 @@ #define EVAL_MOFN 0xea -bool MofNValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool MofNValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue MofNInfo(); diff --git a/src/cc/CCOracles.h b/src/cc/CCOracles.h index 41409d5c5..5e2fcc8b9 100644 --- a/src/cc/CCOracles.h +++ b/src/cc/CCOracles.h @@ -19,7 +19,7 @@ #include "CCinclude.h" -bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string OracleCreate(int64_t txfee,std::string name,std::string description,std::string format); std::string OracleRegister(int64_t txfee,uint256 oracletxid,int64_t datafee); std::string OracleSubscribe(int64_t txfee,uint256 oracletxid,CPubKey publisher,int64_t amount); diff --git a/src/cc/CCPayments.h b/src/cc/CCPayments.h index 7e7a0e681..18f6cb7ef 100644 --- a/src/cc/CCPayments.h +++ b/src/cc/CCPayments.h @@ -19,7 +19,7 @@ #include "CCinclude.h" -bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue PaymentsInfo(); diff --git a/src/cc/CCPegs.h b/src/cc/CCPegs.h index 296e0c272..0e20d278f 100644 --- a/src/cc/CCPegs.h +++ b/src/cc/CCPegs.h @@ -19,7 +19,7 @@ #include "CCinclude.h" -bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue PegsInfo(); diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 8b09e9267..31b13e87b 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -19,7 +19,7 @@ #include "CCinclude.h" -bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue PricesInfo(); diff --git a/src/cc/CCTriggers.h b/src/cc/CCTriggers.h index 4e9881c52..bd243842f 100644 --- a/src/cc/CCTriggers.h +++ b/src/cc/CCTriggers.h @@ -19,7 +19,7 @@ #include "CCinclude.h" -bool TriggersValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool TriggersValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom UniValue TriggersInfo(); diff --git a/src/cc/CCassets.h b/src/cc/CCassets.h index 9a0f2a0ea..9ed16ad02 100644 --- a/src/cc/CCassets.h +++ b/src/cc/CCassets.h @@ -26,7 +26,7 @@ #include "CCinclude.h" // CCcustom -bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCassetsCore //CTxOut MakeAssetsVout(CAmount nValue,CPubKey pk); diff --git a/src/cc/CCauction.h b/src/cc/CCauction.h index 855cdf72e..be636e76b 100644 --- a/src/cc/CCauction.h +++ b/src/cc/CCauction.h @@ -21,7 +21,7 @@ #define EVAL_AUCTION 0xe8 -bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string AuctionPost(uint64_t txfee,uint256 itemhash,int64_t minbid,char *title,char *description); std::string AuctionBid(uint64_t txfee,uint256 itemhash,int64_t amount); diff --git a/src/cc/CCchannels.h b/src/cc/CCchannels.h index 26057457f..30365a379 100644 --- a/src/cc/CCchannels.h +++ b/src/cc/CCchannels.h @@ -20,7 +20,7 @@ #include "CCinclude.h" #define CHANNELS_MAXPAYMENTS 1000 -bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string ChannelOpen(uint64_t txfee,CPubKey destpub,int32_t numpayments,int64_t payment); std::string ChannelStop(uint64_t txfee,CPubKey destpub,uint256 origtxid); std::string ChannelPayment(uint64_t txfee,uint256 prevtxid,uint256 origtxid,int32_t n,int64_t amount); diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index d9a793ddd..a8e9dc0a6 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -29,6 +29,7 @@ #include "CCTriggers.h" #include "CCPayments.h" #include "CCGateways.h" +#include "CoinbaseGuard.h" /* CCcustom has most of the functions that need to be extended to create a new CC contract. @@ -221,11 +222,20 @@ uint8_t GatewaysCCpriv[32] = { 0xf7, 0x4b, 0x5b, 0xa2, 0x7a, 0x5e, 0x9c, 0xda, 0 #undef FUNCNAME #undef EVALCODE -struct CCcontract_info *CCinit(struct CCcontract_info *cp,uint8_t evalcode) +struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) { cp->evalcode = evalcode; switch ( evalcode ) { + case EVAL_COINBASEGUARD: + strcpy(cp->unspendableCCaddr,AssetsCCaddr); + strcpy(cp->normaladdr,AssetsNormaladdr); + strcpy(cp->CChexstr,AssetsCChexstr); + memcpy(cp->CCpriv,AssetsCCpriv,32); + cp->validate = AssetsValidate; + cp->ismyvin = IsAssetsInput; + break; + case EVAL_ASSETS: strcpy(cp->unspendableCCaddr,AssetsCCaddr); strcpy(cp->normaladdr,AssetsNormaladdr); diff --git a/src/cc/CCdice.h b/src/cc/CCdice.h index 16b2f6136..92e2bc0cd 100644 --- a/src/cc/CCdice.h +++ b/src/cc/CCdice.h @@ -21,7 +21,7 @@ #define EVAL_DICE 0xe6 -bool DiceValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +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(int32_t *resultp,uint64_t txfee,char *planstr,uint256 fundingtxid,uint256 bettxid,int32_t winlosetimeout); diff --git a/src/cc/CCfaucet.h b/src/cc/CCfaucet.h index 39930cb20..895327d09 100644 --- a/src/cc/CCfaucet.h +++ b/src/cc/CCfaucet.h @@ -22,7 +22,7 @@ #define EVAL_FAUCET 0xe4 #define FAUCETSIZE (COIN / 10) -bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom std::string FaucetFund(uint64_t txfee,int64_t funds); diff --git a/src/cc/CCfsm.h b/src/cc/CCfsm.h index c6ea17024..0aafdb6b9 100644 --- a/src/cc/CCfsm.h +++ b/src/cc/CCfsm.h @@ -21,7 +21,7 @@ #define EVAL_FSM 0xe7 -bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); std::string FSMList(); std::string FSMInfo(uint256 fsmtxid); diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 4beb722a1..119325240 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -64,13 +64,23 @@ struct CC_utxo int32_t vout; }; +// these are the parameters stored after Verus crypto-condition vouts. new versions may change +// the format +struct CC_meta +{ + std::vector version; + uint8_t evalCode; + bool is1of2; + uint8_t numDestinations; + // followed by address destinations +}; + struct CCcontract_info { - uint256 prevtxid; char unspendableCCaddr[64],CChexstr[72],normaladdr[64],unspendableaddr2[64],unspendableaddr3[64]; uint8_t CCpriv[32],unspendablepriv2[32],unspendablepriv3[32]; CPubKey unspendablepk2,unspendablepk3; - bool (*validate)(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); + bool (*validate)(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn); bool (*ismyvin)(CScript const& scriptSig); uint8_t evalcode,evalcode2,evalcode3,didinit; }; @@ -102,6 +112,7 @@ int32_t iguana_rwbignum(int32_t rwflag,uint8_t *serialized,int32_t len,uint8_t * CScript GetScriptForMultisig(int nRequired, const std::vector& keys); int64_t CCaddress_balance(char *coinaddr); CPubKey CCtxidaddr(char *txidaddr,uint256 txid); +bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn, std::vector> &preConditions, std::vector> ¶ms); int64_t OraclePrice(int32_t height,uint256 reforacletxid,char *markeraddr,char *format); uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format); @@ -125,7 +136,8 @@ uint256 DiceHashEntropy(uint256 &entropy,uint256 _txidpriv); CTxOut MakeCC1vout(uint8_t evalcode,CAmount nValue,CPubKey pk); CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk,CPubKey pk2); CC *MakeCCcond1(uint8_t evalcode,CPubKey pk); -CC* GetCryptoCondition(CScript const& scriptSig); +CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2); +CC *GetCryptoCondition(CScript const& scriptSig); void CCaddr2set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); void CCaddr3set(struct CCcontract_info *cp,uint8_t evalcode,CPubKey pk,uint8_t *priv,char *coinaddr); bool IsCCInput(CScript const& scriptSig); @@ -149,6 +161,7 @@ bool Myprivkey(uint8_t myprivkey[]); int64_t CCduration(int32_t &numblocks,uint256 txid); // CCtx +bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey); std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey mypk,uint64_t txfee,CScript opret); void SetCCunspents(std::vector > &unspentOutputs,char *coinaddr); void SetCCtxids(std::vector > &addressIndex,char *coinaddr); diff --git a/src/cc/CClotto.h b/src/cc/CClotto.h index 784441c57..838aac338 100644 --- a/src/cc/CClotto.h +++ b/src/cc/CClotto.h @@ -21,7 +21,7 @@ #define EVAL_LOTTO 0xe9 -bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); UniValue LottoInfo(uint256 lottoid); UniValue LottoList(); diff --git a/src/cc/CCrewards.h b/src/cc/CCrewards.h index ef675797c..ac90423b2 100644 --- a/src/cc/CCrewards.h +++ b/src/cc/CCrewards.h @@ -22,7 +22,7 @@ #define EVAL_REWARDS 0xe5 #define REWARDSCC_MAXAPR (COIN * 25) -bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx); +bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); UniValue RewardsInfo(uint256 rewardid); UniValue RewardsList(); diff --git a/src/cc/CCutils.cpp b/src/cc/CCutils.cpp index 5b7e01304..4b465e44e 100644 --- a/src/cc/CCutils.cpp +++ b/src/cc/CCutils.cpp @@ -197,6 +197,32 @@ bool Getscriptaddress(char *destaddr,const CScript &scriptPubKey) return(false); } +bool GetCCParams(Eval* eval, const CTransaction &tx, uint32_t nIn, std::vector> &preConditions, std::vector> ¶ms) +{ + CTransaction txOut; + uint256 blockHash; + bool isValid = false; + + if (myGetTransaction(tx.vin[nIn].prevout.hash, txOut, blockHash)) + { + CBlockIndex index; + if (eval->GetBlock(blockHash, index)) + { + // read preconditions + CScript subScript = CScript(); + preConditions.clear(); + if (txOut.vout[tx.vin[nIn].prevout.n].scriptPubKey.IsPayToCryptoCondition(&subScript, preConditions)) + { + // read any available parameters in the output transaction + params.clear(); + tx.vout[tx.vout.size() - 1].scriptPubKey.GetOpretData(params); + isValid = true; + } + } + } + return isValid; +} + CPubKey CCtxidaddr(char *txidaddr,uint256 txid) { uint8_t buf33[33]; CPubKey pk; @@ -361,7 +387,7 @@ bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector param return eval->Invalid("Cannot have params"); else if ( ctx.vout.size() == 0 ) return eval->Invalid("no-vouts"); - else if ( (*cp->validate)(cp,eval,ctx) != 0 ) + else if ( (*cp->validate)(cp,eval,ctx,nIn) != 0 ) { //fprintf(stderr,"done CC %02x\n",cp->evalcode); //cp->prevtxid = txid; diff --git a/src/cc/CoinbaseGuard.cpp b/src/cc/CoinbaseGuard.cpp new file mode 100644 index 000000000..b7332bdc3 --- /dev/null +++ b/src/cc/CoinbaseGuard.cpp @@ -0,0 +1,254 @@ +/******************************************************************** + * (C) 2018 Michael Toutonghi + * + * Distributed under the MIT software license, see the accompanying + * file COPYING or http://www.opensource.org/licenses/mit-license.php. + * + * This crypto-condition eval solves the problem of nothing-at-stake + * in a proof of stake consensus system. + * + */ + +#include "CoinbaseGuard.h" +#include "main.h" + +extern int32_t VERUS_MIN_STAKEAGE; + +bool UnpackStakeOpRet(const CTransaction &stakeTx, std::vector> vData) +{ + bool isValid = stakeTx.vout[stakeTx.vout.size() - 1].scriptPubKey.GetOpretData(vData); + + if (isValid && (vData.size() >= CStakeParams::STAKE_MINPARAMS) && (vData.size() <= CStakeParams::STAKE_MAXPARAMS)) + { + return true; + } + return false; +} + +CStakeParams::CStakeParams(std::vector> vData) +{ + // A stake OP_RETURN contains: + // 1. source block height in little endian 32 bit + // 2. target block height in little endian 32 bit + // 3. 32 byte prev block hash + // 4. alternate 20 byte pubkey hash, 33 byte pubkey, or not present to use same as stake destination + + srcHeight = 0; + blkHeight = 0; + if (vData[0].size() == 1 && + vData[0][0] == OPRETTYPE_STAKEPARAMS && vData[1].size() <= 4 && + vData[2].size() <= 4 && + vData[3].size() == sizeof(prevHash) && + (vData.size() == STAKE_MINPARAMS || vData[4].size() == 20 || vData[5].size() == 33)) + { + for (auto ch : vData[1]) + { + srcHeight = srcHeight << 8 | ch; + } + for (auto ch : vData[2]) + { + blkHeight = blkHeight << 8 | ch; + } + + prevHash = uint256(vData[3]); + + if (vData.size() == 4) + { + dest = CTxDestination(); + } + else if (vData[4].size() == 20) + { + dest = CTxDestination(CKeyID(uint160(vData[4]))); + } + else if (vData[4].size() == 33) + { + CPubKey pk = CPubKey(vData[4]); + if (pk.IsValid()) + { + dest = pk; + } + else + { + // invalidate + srcHeight = 0; + } + } + else + { + // invalidate + srcHeight = 0; + } + } +} + +// this validates everything, except the PoS eligibility and the actual stake spend. the only time it matters +// is to validate a properly formed stake transaction for either pre-check before PoS validity check, or to +// validate the stake transaction on a fork that will be used to spend a winning stake that cheated by being posted +// on two fork chains +bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams) +{ + std::vector> vData = std::vector>(); + + // a valid stake transaction has one input and two outputs, one output is the monetary value and one is an op_ret with CStakeParams + // stake output #1 must be P2PK or P2PKH, unless a delegate for the coinbase is specified + + bool isValid = false;; + if (stakeTx.vin.size() == 1 && + stakeTx.vout.size() == 2 && + stakeTx.vout[0].nValue > 0 && + stakeTx.vout[1].scriptPubKey.IsOpReturn() && + UnpackStakeOpRet(stakeTx, vData)) + { + stakeParams = CStakeParams(vData); + if (stakeParams.IsValid()) + { + // if we have gotten this far and are still valid, we need to validate everything else + // even if the utxo is spent, this can succeed, as it only checks that is was ever valid + CTransaction srcTx = CTransaction(); + uint256 blkHash = uint256(); + txnouttype txType; + CBlockIndex *pindex; + if (isValid && myGetTransaction(stakeTx.vin[0].prevout.hash, srcTx, blkHash)) + { + isValid = false; + if ((pindex = mapBlockIndex[blkHash]) != NULL) + { + std::vector> vAddr = std::vector>(); + + if (stakeParams.srcHeight == pindex->GetHeight() && + (stakeParams.blkHeight - stakeParams.srcHeight >= VERUS_MIN_STAKEAGE) && + Solver(srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, txType, vAddr)) + { + if (txType == TX_PUBKEY) + { + if (stakeParams.dest.which() == 0) + { + stakeParams.dest = CPubKey(vAddr[0]); + } + } + else if (txType == TX_PUBKEYHASH) + { + if (stakeParams.dest.which() == 0) + { + stakeParams.dest = CKeyID(uint160(vAddr[0])); + } + } + if ((txType == TX_PUBKEY) && (txType == TX_PUBKEYHASH)) + { + auto consensusBranchId = CurrentEpochBranchId(stakeParams.blkHeight, Params().GetConsensus()); + isValid = VerifyScript(stakeTx.vin[0].scriptSig, + srcTx.vout[stakeTx.vin[0].prevout.n].scriptPubKey, + STANDARD_SCRIPT_VERIFY_FLAGS + SCRIPT_VERIFY_SIGPUSHONLY, + BaseSignatureChecker(), + consensusBranchId); + } + } + } + } + else + { + isValid = false; + } + } + } + return isValid; +} + +bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout) +{ + CCcontract_info *cp, C; + cp = CCinit(&C,EVAL_COINBASEGUARD); + + CPubKey ccAddress = CPubKey(ParseHex(cp->CChexstr)); + + // return an output that is bound to the stake transaction and can be spent by presenting either a signed condition by the original + // destination address or a properly signed stake transaction of the same utxo on a fork + vout = MakeCC1of2vout(EVAL_COINBASEGUARD, value, dest, ccAddress); + + // add parameters to scriptPubKey + COptCCParams p = COptCCParams(COptCCParams::VERSION, EVAL_COINBASEGUARD, 1, 2); + + std::vector a1, a2; + CKeyID id1 = dest.GetID(); + CKeyID id2 = ccAddress.GetID(); + a1 = std::vector(id1.begin(), id1.end()); + a2 = std::vector(id2.begin(), id2.end()); + + // version + // utxo source hash + // utxo source output + // hashed address of destination's pubkey + CKeyID key = dest.GetID(); + vout.scriptPubKey << p.AsVector() << OP_DROP + << a1 << OP_DROP << a2 << OP_DROP + << std::vector(stakeTx.vin[0].prevout.hash.begin(), stakeTx.vin[0].prevout.hash.end()) << OP_DROP + << stakeTx.vin[0].prevout.n << OP_DROP; + + return false; +} + +// This is only needed to create a spend for cheating. normal spend and signing should work +// for vins +bool MakeGuardedSpend(CTxIn &vin, CPubKey &dest, CTransaction *pCheater) +{ + CCcontract_info *cp,C; + + cp = CCinit(&C,EVAL_COINBASEGUARD); + CC cc; + vin.scriptSig = CCPubKey(MakeCCcond1of2(EVAL_COINBASEGUARD, dest, CPubKey(ParseHex(cp->CChexstr)))); +} + +bool CoinbaseGuardValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn) +{ + // This also supports a variable blockstomaturity option for backward feature compatibility + // validate this spend of a transaction with it being past any applicable time lock and one of the following statements being true: + // 1. the spend is signed by the original output destination's private key and normal payment requirements, spends as normal + // 2. the spend is signed by the private key of the CoinbaseGuard contract and pushes a signed stake transaction + // with the same exact utxo source, a target block height of later than that of this tx that is also targeting a fork + // of the chain + + // first, check to see if the spending contract is signed by the default destination address + // if so, success and we are done + + // get preConditions and parameters + std::vector> preConditions = std::vector>(); + std::vector> params = std::vector>(); + + if (GetCCParams(eval, tx, nIn, preConditions, params)) + { + CC *cc = GetCryptoCondition(tx.vin[nIn].scriptSig); + + printf("CryptoCondition code %x\n", *cc->code); + // check any applicable time lock + // determine who signed + // if from receiver's priv key, success + // if from contract priv key: + // if data provided is valid stake spend of same utxo targeting same or later block height on a fork: + // return success + // endif + // endif + // return fail + cc_free(cc); + } +} + +UniValue CoinbaseGuardInfo() +{ + UniValue result(UniValue::VOBJ); char numstr[64]; + CMutableTransaction mtx; + CPubKey pk; + + CCcontract_info *cp,C; + + cp = CCinit(&C,EVAL_COINBASEGUARD); + + result.push_back(Pair("result","success")); + result.push_back(Pair("name","CoinbaseGuard")); + + // all UTXOs to the contract address that are to any of the wallet addresses are to us + // each is spendable as a normal transaction, but the spend may fail if it gets spent out + // from under us + pk = GetUnspendable(cp,0); + return(result); +} + diff --git a/src/cc/CoinbaseGuard.h b/src/cc/CoinbaseGuard.h new file mode 100644 index 000000000..9fdb562a5 --- /dev/null +++ b/src/cc/CoinbaseGuard.h @@ -0,0 +1,47 @@ +/******************************************************************** + * (C) 2018 Michael Toutonghi + * + * Distributed under the MIT software license, see the accompanying + * file COPYING or http://www.opensource.org/licenses/mit-license.php. + * + * This crypto-condition eval solves the problem of nothing-at-stake + * in a proof of stake consensus system. + * + */ + +#ifndef CC_COINBASEGUARD_H +#define CC_COINBASEGUARD_H + +#include + +#include "CCinclude.h" +#include "streams.h" +#include "script/script.h" + +class CStakeParams +{ + public: + static const uint32_t STAKE_MINPARAMS = 4; + static const uint32_t STAKE_MAXPARAMS = 5; + + uint32_t srcHeight; + uint32_t blkHeight; + uint256 prevHash; + CTxDestination dest; + + CStakeParams() : srcHeight(0), blkHeight(0), prevHash(), dest() {} + + CStakeParams(std::vector> vData); + + bool IsValid() { return srcHeight != 0; } +}; + +bool ValidateStakeTransaction(const CTransaction &stakeTx, CStakeParams &stakeParams); + +bool MakeGuardedOutput(CAmount value, CPubKey &dest, CTransaction &stakeTx, CTxOut &vout); + +bool CoinbaseGuardValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); + +UniValue CoinbaseGuardInfo(); + +#endif diff --git a/src/cc/MofN.cpp b/src/cc/MofN.cpp index 84952176f..0a038815c 100644 --- a/src/cc/MofN.cpp +++ b/src/cc/MofN.cpp @@ -85,7 +85,7 @@ bool MofNExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & else return(true); } -bool MofNValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool MofNValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; return(false); diff --git a/src/cc/assets.cpp b/src/cc/assets.cpp index e5a5af4e0..5f856563d 100644 --- a/src/cc/assets.cpp +++ b/src/cc/assets.cpp @@ -129,7 +129,7 @@ vout.n-1: opreturn [EVAL_ASSETS] ['E'] [assetid vin0+1] [assetid vin2] [remaining asset2 required] [origpubkey] */ -bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool AssetsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { static uint256 zero; CTxDestination address; CTransaction vinTx,createTx; uint256 hashBlock,assetid,assetid2; int32_t i,starti,numvins,numvouts,preventCCvins,preventCCvouts; int64_t remaining_price,nValue,assetoshis,outputs,inputs,tmpprice,totalunits,ignore; std::vector origpubkey,tmporigpubkey,ignorepubkey; uint8_t funcid; char destaddr[64],origaddr[64],CCaddr[64]; diff --git a/src/cc/auction.cpp b/src/cc/auction.cpp index 7b5f106d0..a47785cb5 100644 --- a/src/cc/auction.cpp +++ b/src/cc/auction.cpp @@ -70,7 +70,7 @@ bool AuctionExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransactio else return(true); } -bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool AuctionValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval; return(false); // reject any auction CC for now diff --git a/src/cc/channels.cpp b/src/cc/channels.cpp index dc1a3f291..b6c221c85 100644 --- a/src/cc/channels.cpp +++ b/src/cc/channels.cpp @@ -111,7 +111,7 @@ bool ChannelsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti else return(true); } -bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool ChannelsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; return(false); diff --git a/src/cc/dice.cpp b/src/cc/dice.cpp index 4a3cf4f9b..a0359dbc6 100644 --- a/src/cc/dice.cpp +++ b/src/cc/dice.cpp @@ -439,7 +439,7 @@ bool DiceVerifyTimeout(CTransaction &betTx,int32_t timeoutblocks) return(numblocks >= timeoutblocks); } -bool DiceValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx) +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,sbits,amount,inputs,outputs,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,iswin; uint8_t funcid; CScript fundingPubKey; CTransaction fundingTx,vinTx,vinofvinTx; char CCaddr[64]; numvins = tx.vin.size(); diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 1731436af..b2b129648 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -34,9 +34,6 @@ extern pthread_mutex_t KOMODO_CC_mutex; bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn) { - // DISABLE CRYPTO CONDITIONS FOR NOW - return false; - EvalRef eval; pthread_mutex_lock(&KOMODO_CC_mutex); bool out = eval->Dispatch(cond, tx, nIn); @@ -77,15 +74,17 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) switch ( ecode ) { case EVAL_IMPORTPAYOUT: - return ImportPayout(vparams, txTo, nIn); + //return ImportPayout(vparams, txTo, nIn); break; case EVAL_IMPORTCOIN: - return ImportCoin(vparams, txTo, nIn); + //return ImportCoin(vparams, txTo, nIn); break; default: - return(ProcessCC(cp,this, vparams, txTo, nIn)); + // only support coinbase guard for now + if (ecode == EVAL_COINBASEGUARD) + return(ProcessCC(cp,this, vparams, txTo, nIn)); break; } return Invalid("invalid-code, dont forget to add EVAL_NEWCC to Eval::Dispatch"); diff --git a/src/cc/eval.h b/src/cc/eval.h index ee83df979..8f4d5d412 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -38,6 +38,7 @@ * after the code is interpreted as a bitcoin script. */ #define FOREACH_EVAL(EVAL) \ + EVAL(EVAL_COINBASEGUARD, 0x1) \ EVAL(EVAL_IMPORTPAYOUT, 0xe1) \ EVAL(EVAL_IMPORTCOIN, 0xe2) \ EVAL(EVAL_ASSETS, 0xe3) \ diff --git a/src/cc/faucet.cpp b/src/cc/faucet.cpp index 676cb152c..76fb631e1 100644 --- a/src/cc/faucet.cpp +++ b/src/cc/faucet.cpp @@ -77,7 +77,7 @@ bool FaucetExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction else return(true); } -bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool FaucetValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; std::vector > txids; diff --git a/src/cc/fsm.cpp b/src/cc/fsm.cpp index 41601b437..fc51ed4aa 100644 --- a/src/cc/fsm.cpp +++ b/src/cc/fsm.cpp @@ -72,7 +72,7 @@ bool FSMExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &t else return(true); } -bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool FSMValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval; return(false); // reject any FSM CC for now diff --git a/src/cc/gateways.cpp b/src/cc/gateways.cpp index f21ee7c03..5ee05c4fa 100644 --- a/src/cc/gateways.cpp +++ b/src/cc/gateways.cpp @@ -239,7 +239,7 @@ bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti else return(true); } -bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx) +bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; std::vector > txids; diff --git a/src/cc/lotto.cpp b/src/cc/lotto.cpp index 7129bfcd8..9fa4daa24 100644 --- a/src/cc/lotto.cpp +++ b/src/cc/lotto.cpp @@ -112,7 +112,7 @@ bool LottoExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction else return(true); } -bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool LottoValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; bool retval; return(false); // reject any lotto CC for now diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index c24ad5a8b..a8ac77206 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -593,7 +593,7 @@ bool OraclesDataValidate(struct CCcontract_info *cp,Eval* eval,const CTransactio else return(true); } -bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { uint256 txid,oracletxid,batontxid; uint64_t txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts; uint8_t *script; std::vector vopret,data; CScript scriptPubKey; CPubKey publisher; numvins = tx.vin.size(); diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index 6cd751b8d..fcf2da78e 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -72,7 +72,7 @@ bool PaymentsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti else return(true); } -bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool PaymentsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; return(false); diff --git a/src/cc/pegs.cpp b/src/cc/pegs.cpp index d9074bd49..6df6d18d6 100644 --- a/src/cc/pegs.cpp +++ b/src/cc/pegs.cpp @@ -79,7 +79,7 @@ bool PegsExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction & else return(true); } -bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool PegsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; return(false); diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 1465c6ee6..b732c994c 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -91,7 +91,7 @@ bool PricesExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction else return(true); } -bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; return(false); diff --git a/src/cc/rewards.cpp b/src/cc/rewards.cpp index a70071af9..0f524756d 100644 --- a/src/cc/rewards.cpp +++ b/src/cc/rewards.cpp @@ -192,7 +192,7 @@ bool RewardsExactAmounts(struct CCcontract_info *cp,Eval *eval,const CTransactio else return(true); } -bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool RewardsValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { uint256 txid,fundingtxid,hashBlock,vinfundingtxid; uint64_t vinsbits,sbits,APR,minseconds,maxseconds,mindeposit,amount,reward,txfee=10000; int32_t numvins,numvouts,preventCCvins,preventCCvouts,i; uint8_t funcid; CScript scriptPubKey; CTransaction fundingTx,vinTx; numvins = tx.vin.size(); diff --git a/src/cc/triggers.cpp b/src/cc/triggers.cpp index 93107f4a9..89dc4e468 100644 --- a/src/cc/triggers.cpp +++ b/src/cc/triggers.cpp @@ -71,7 +71,7 @@ bool TriggersExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransacti else return(true); } -bool TriggersValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) +bool TriggersValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; return(false); diff --git a/src/crypto/verus_hash.h b/src/crypto/verus_hash.h index 07c76d0f5..5ac49c209 100644 --- a/src/crypto/verus_hash.h +++ b/src/crypto/verus_hash.h @@ -1,4 +1,4 @@ -// (C) 2018 The Verus Developers +// (C) 2018 Michael Toutonghi // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. diff --git a/src/script/cc.cpp b/src/script/cc.cpp index 965fae4b0..ce9262295 100644 --- a/src/script/cc.cpp +++ b/src/script/cc.cpp @@ -86,6 +86,14 @@ CScript CCSig(const CC *cond) return CScript() << ffill; } +std::vector CCSigVec(const CC *cond) +{ + unsigned char buf[10000]; + size_t len = cc_fulfillmentBinary(cond, buf, 10000); + auto ffill = std::vector(buf, buf+len); + ffill.push_back(1); // SIGHASH_ALL + return ffill; +} std::string CCShowStructure(CC *cond) { diff --git a/src/script/cc.h b/src/script/cc.h index 246f19052..583769a38 100644 --- a/src/script/cc.h +++ b/src/script/cc.h @@ -57,6 +57,12 @@ CScript CCPubKey(const CC *cond); */ CScript CCSig(const CC *cond); +/* + * Turn a condition into a scriptSig + * + * Note: This will fail in undefined ways if the condition is missing signatures + */ +std::vector CCSigVec(const CC *cond); /* * Produces a string showing the structure of a CC condition diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 93f5cd76b..b57cef4e3 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -952,7 +952,7 @@ bool EvalScript( if (stack.size() < 2) return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION); -//fprintf(stderr,"check cryptocondition\n"); + //fprintf(stderr,"check cryptocondition\n"); int fResult = checker.CheckCryptoCondition(stacktop(-1), stacktop(-2), script, consensusBranchId); if (fResult == -1) { return set_error(serror, SCRIPT_ERR_CRYPTOCONDITION_INVALID_FULFILLMENT); diff --git a/src/script/script.cpp b/src/script/script.cpp index f2f1201a5..5f56d15a1 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -245,9 +245,64 @@ bool CScript::IsPayToScriptHash() const (*this)[22] == OP_EQUAL); } -bool CScript::IsPayToCryptoCondition() const +// this returns true if either there is nothing left and pc points at the end, or +// all instructions from the pc to the end of the script are balanced pushes and pops +// if there is data, it also returns all the values as byte vectors in a list of vectors +bool CScript::GetBalancedData(const_iterator& pc, std::vector>& vSolutions) const { - const_iterator pc = this->begin(); + int netPushes = 0; + vSolutions.clear(); + + while (pc < end()) + { + vector data; + opcodetype opcode; + if (this->GetOp(pc, opcode, data)) + { + if (opcode == OP_DROP) + { + // this should never pop what it hasn't pushed (like a success code) + if (--netPushes < 0) + return false; + } + if (opcode < 1 || opcode > OP_PUSHDATA4) + return false; + netPushes++; + vSolutions.push_back(data); + } + else + return false; + } + return netPushes == 0; +} + +// this returns true if either there is nothing left and pc points at the end, or +// all instructions from the pc to the end of the script are balanced pushes and pops +// if there is data, it also returns all the values as byte vectors in a list of vectors +bool CScript::GetOpretData(std::vector>& vData) const +{ + vector data; + opcodetype opcode; + const_iterator pc = begin(); + + vData.clear(); + + if (GetOp(pc, opcode, data) && opcode == OP_RETURN) + { + while (pc < end()) + { + if (GetOp(pc, opcode, data)) + { + vData.push_back(data); + } + } + return vData.size() != 0; + } +} + +bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector>& vSolutions) const +{ + const_iterator pc = begin(); vector data; opcodetype opcode; if (this->GetOp(pc, opcode, data)) @@ -255,9 +310,27 @@ bool CScript::IsPayToCryptoCondition() const if (opcode > OP_0 && opcode < OP_PUSHDATA1) if (this->GetOp(pc, opcode, data)) if (opcode == OP_CHECKCRYPTOCONDITION) - if (pc == this->end()) + { + const_iterator pcCCEnd = pc; + if (GetBalancedData(pc, vSolutions)) + { + if (pCCSubScript) + *pCCSubScript = CScript(begin(),pc); return 1; - return 0; + } + } + return false; +} + +bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript) const +{ + std::vector> vSolutions; + return IsPayToCryptoCondition(pCCSubScript, vSolutions); +} + +bool CScript::IsPayToCryptoCondition() const +{ + return IsPayToCryptoCondition(NULL); } bool CScript::MayAcceptCryptoCondition() const diff --git a/src/script/script.h b/src/script/script.h index 5b8a1ada0..e15d58bb4 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -19,6 +19,7 @@ #include #define OPRETTYPE_TIMELOCK 1 +#define OPRETTYPE_STAKEPARAMS 2 static const unsigned int MAX_SCRIPT_ELEMENT_SIZE = 520; // bytes @@ -381,6 +382,7 @@ protected: } return *this; } + bool GetBalancedData(const_iterator& pc, std::vector>& vSolutions) const; public: CScript() { } CScript(const CScript& b) : CScriptBase(b.begin(), b.end()) { } @@ -574,6 +576,11 @@ public: bool IsPayToPublicKey() const; bool IsPayToScriptHash() const; + bool IsOpReturn() const { return size() > 0 && (*this)[0] == OP_RETURN; } + bool GetOpretData(std::vector>& vData) const; + + bool IsPayToCryptoCondition(CScript *ccSubScript, std::vector>& vSolutions) const; + bool IsPayToCryptoCondition(CScript *ccSubScript) const; bool IsPayToCryptoCondition() const; bool IsCoinImport() const; bool MayAcceptCryptoCondition() const; diff --git a/src/script/sign.cpp b/src/script/sign.cpp index 2cf3aa53a..dec3f704e 100644 --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -10,6 +10,8 @@ #include "keystore.h" #include "script/standard.h" #include "uint256.h" +#include "cc/CCinclude.h" +#include "cc/eval.h" #include @@ -19,10 +21,12 @@ typedef std::vector valtype; TransactionSignatureCreator::TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn) : BaseSignatureCreator(keystoreIn), txTo(txToIn), nIn(nInIn), nHashType(nHashTypeIn), amount(amountIn), checker(txTo, nIn, amountIn) {} -bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, uint32_t consensusBranchId) const +bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, const CKeyID& address, const CScript& scriptCode, uint32_t consensusBranchId, CKey *pprivKey, void *extraData) const { CKey key; - if (!keystore->GetKey(address, key)) + if (pprivKey) + key = *pprivKey; + else if (!keystore->GetKey(address, key)) return false; uint256 hash; @@ -32,8 +36,19 @@ bool TransactionSignatureCreator::CreateSig(std::vector& vchSig, return false; } - if (!key.Sign(hash, vchSig)) - return false; + if (scriptCode.IsPayToCryptoCondition()) + { + CC *cc = (CC *)extraData; + // assume either 1of1 or 1of2. if the condition created by the + if (!cc || cc_signTreeSecp256k1Msg32(cc, key.begin(), hash.begin()) == 0) + return false; + vchSig = CCSigVec(cc); + } + else + { + if (!key.Sign(hash, vchSig)) + return false; + } vchSig.push_back((unsigned char)nHashType); return true; } @@ -61,6 +76,201 @@ static bool SignN(const vector& multisigdata, const BaseSignatureCreato return nSigned==nRequired; } +CC *CCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2) +{ + std::vector pks; + pks.push_back(CCNewSecp256k1(pk1)); + pks.push_back(CCNewSecp256k1(pk2)); + CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode)); + CC *Sig = CCNewThreshold(1, pks); + return CCNewThreshold(2, {condCC, Sig}); +} + +CC *CCcond1(uint8_t evalcode,CPubKey pk) +{ + std::vector pks; + pks.push_back(CCNewSecp256k1(pk)); + CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode)); + CC *Sig = CCNewThreshold(1, pks); + return CCNewThreshold(2, {condCC, Sig}); +} + +bool CCinitLite(struct CCcontract_info *cp, uint8_t evalcode) +{ + cp->evalcode = evalcode; + switch ( evalcode ) + { + case EVAL_COINBASEGUARD: + uint8_t privKey[32] = { 0x9b, 0x17, 0x66, 0xe5, 0x82, 0x66, 0xac, 0xb6, 0xba, 0x43, 0x83, 0x74, 0xf7, 0x63, 0x11, 0x3b, 0xf0, 0xf3, 0x50, 0x6f, 0xd9, 0x6b, 0x67, 0x85, 0xf9, 0x7a, 0xf0, 0x54, 0x4d, 0xb1, 0x30, 0x77 }; + strcpy(cp->unspendableCCaddr,"RGKRjeTBw4LYFotSDLT6RWzMHbhXri6BG6"); + strcpy(cp->normaladdr,"RFYE2yL3KknWdHK6uNhvWacYsCUtwzjY3u"); + strcpy(cp->CChexstr,"02adf84e0e075cf90868bd4e3d34a03420e034719649c41f371fc70d8e33aa2702"); + memcpy(cp->CCpriv, privKey,32); + return true; + } + return false; +} + +static bool SignStepCC(const BaseSignatureCreator& creator, const CScript& scriptPubKey, vector &vSolutions, + vector& ret, uint32_t consensusBranchId) +{ + CScript subScript; + vector vPK; + vector vKeyID = vector(); + vector vParams = vector(); + COptCCParams p; + + scriptPubKey.IsPayToCryptoCondition(&subScript, vParams); + if (vParams.size() > 1 && (p = COptCCParams(vParams[0])).IsValid()) + { + bool is1of2 = (p.n == 1 && p.m == 1); + uint32_t extraAddrs = p.m; + CKey privKey; + + // get information to sign with + CCcontract_info C; + + // must be a valid cc eval code + if (CCinitLite(&C, p.evalCode)) + { + // pay to cc address is a valid tx + if (!is1of2) + { + try + { + if (!extraAddrs) + { + vKeyID.push_back(CKeyID(uint160(vSolutions[0]))); + // if this isn't our main CC address, we can't do anything with it + if (strcmp(C.unspendableCCaddr, CBitcoinAddress(CTxDestination(vKeyID[0])).ToString().c_str()) != 0) + return false; + + // otherwise, push back the corresponding pub key + vPK.push_back(CPubKey(ParseHex(C.CChexstr))); + } + else if (vParams.size() >= (extraAddrs + 1)) + { + bool havePriv; + vKeyID.push_back(CKeyID(uint160(vParams[1]))); + // if this isn't the normal CC address and we also don't have it in our keystore, fail + CBitcoinAddress addr = CBitcoinAddress(CTxDestination(vKeyID[0])); + if (strcmp(C.normaladdr, addr.ToString().c_str()) == 0 && + !(havePriv = creator.KeyStore().GetKey(vKeyID[0], privKey))) + return false; + + vPK.push_back(CPubKey()); + + // if we don't have the private key, it is the unspendable address + if (!havePriv) + { + vPK[0] = CPubKey(ParseHex(C.CChexstr)); + privKey = CKey(); + CPrivKey vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv)); + privKey.SetPrivKey(vch, false); + } + else if (!creator.KeyStore().GetPubKey(vKeyID[0], vPK[0])) + return false; + } + } catch (...) + { + fprintf(stderr,"exception calculating 1of1 spend\n"); + return false; + } + + CC *cc = CCcond1(p.evalCode, vPK[0]); + + if (cc) + { + vector vch; + if (creator.CreateSig(vch, vKeyID[0], scriptPubKey, consensusBranchId, &privKey, (void *)cc)) + { + ret.push_back(vch); + } + else + { + fprintf(stderr,"vin has 1of1 CC signing error with address.(%s)\n", vKeyID[0].ToString().c_str()); + } + + cc_free(cc); + return ret.size() != 0; + } + } + else if (extraAddrs > 1 && vParams.size() >= (extraAddrs + 1)) + { + // we need to get 2 addresses, and we will need the private key for one + // to spend + bool pkValid = false; + for (int i = 0; i < extraAddrs; i++) + { + // loop through in order and choose the first key we have a priv key to for signing + try + { + bool isCCAddr = false; + CPubKey pk; + vKeyID.push_back(CKeyID(uint160(vParams[i + 1]))); + + // if this isn't the CC address and we also don't have the pubkey in our keystore, fail, because we won't + // be able to make the condition to fulfill + if (!(isCCAddr = (strcmp(C.normaladdr, CBitcoinAddress(CTxDestination(vKeyID[0])).ToString().c_str()) == 0)) && + !creator.KeyStore().GetPubKey(vKeyID[0], pk)) + return false; + + if (isCCAddr) + { + pk = CPubKey(ParseHex(C.CChexstr)); + // only set the private key to this address if we don't have one yet + if (!pkValid) + { + privKey = CKey(); + CPrivKey vch(&(C.CCpriv[0]), C.CCpriv + sizeof(C.CCpriv)); + privKey.SetPrivKey(vch, false); + pkValid = true; + } + } + else + { + if (!pkValid) + { + if (creator.KeyStore().GetKey(vKeyID[0], privKey)) + pkValid = true; + } + } + + vPK.push_back(pk); + + } catch (...) + { + fprintf(stderr,"exception calculating 1of2 spend\n"); + return false; + } + } + + if (!pkValid) + return false; + + CC *cc = CCcond1of2(p.evalCode, vPK[0], vPK[1]); + + if (cc) + { + vector vch; + if (creator.CreateSig(vch, vKeyID[0], scriptPubKey, consensusBranchId, &privKey, (void *)cc)) + { + ret.push_back(vch); + } + else + { + fprintf(stderr,"vin has 1of2 CC signing error with address.(%s)\n", vKeyID[0].ToString().c_str()); + } + + cc_free(cc); + return ret.size() != 0; + } + } + } + } + return false; +} + /** * Sign scriptPubKey using signature made with creator. * Signatures are returned in scriptSigRet (or returns false if scriptPubKey can't be signed), @@ -96,6 +306,7 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP } CKeyID keyID; + switch (whichTypeRet) { case TX_NONSTANDARD: @@ -121,6 +332,9 @@ static bool SignStep(const BaseSignatureCreator& creator, const CScript& scriptP return true; } return false; + + case TX_CRYPTOCONDITION: + return SignStepCC(creator, scriptPubKey, vSolutions, ret, consensusBranchId); case TX_MULTISIG: ret.push_back(valtype()); // workaround CHECKMULTISIG bug @@ -384,7 +598,9 @@ bool DummySignatureCreator::CreateSig( std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, - uint32_t consensusBranchId) const + uint32_t consensusBranchId, + CKey *key, + void *extraData) const { // Create a dummy signature that is a valid DER-encoding vchSig.assign(72, '\000'); diff --git a/src/script/sign.h b/src/script/sign.h index f531ad0d8..45e469efc 100644 --- a/src/script/sign.h +++ b/src/script/sign.h @@ -8,6 +8,7 @@ #include "script/interpreter.h" +class CKey; class CKeyID; class CKeyStore; class CScript; @@ -27,7 +28,12 @@ public: virtual const BaseSignatureChecker& Checker() const =0; /** Create a singular (non-script) signature. */ - virtual bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const =0; + virtual bool CreateSig(std::vector& vchSig, + const CKeyID& keyid, + const CScript& scriptCode, + uint32_t consensusBranchId, + CKey *key = NULL, + void *extraData = NULL) const = 0; }; /** A signature creator for transactions. */ @@ -41,7 +47,7 @@ class TransactionSignatureCreator : public BaseSignatureCreator { public: TransactionSignatureCreator(const CKeyStore* keystoreIn, const CTransaction* txToIn, unsigned int nInIn, const CAmount& amountIn, int nHashTypeIn=SIGHASH_ALL); const BaseSignatureChecker& Checker() const { return checker; } - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId, CKey *key = NULL, void *extraData = NULL) const; }; class MutableTransactionSignatureCreator : public TransactionSignatureCreator { @@ -56,7 +62,7 @@ class DummySignatureCreator : public BaseSignatureCreator { public: DummySignatureCreator(const CKeyStore* keystoreIn) : BaseSignatureCreator(keystoreIn) {} const BaseSignatureChecker& Checker() const; - bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId) const; + bool CreateSig(std::vector& vchSig, const CKeyID& keyid, const CScript& scriptCode, uint32_t consensusBranchId, CKey *key = NULL, void *extraData = NULL) const; }; struct SignatureData { diff --git a/src/script/standard.cpp b/src/script/standard.cpp index 673c4ce20..6bda93682 100644 --- a/src/script/standard.cpp +++ b/src/script/standard.cpp @@ -73,19 +73,37 @@ bool Solver(const CScript& scriptPubKey, txnouttype& typeRet, vector> vParams; + if (scriptPubKey.IsPayToCryptoCondition(&ccSubScript, vParams)) { if (scriptPubKey.MayAcceptCryptoCondition()) { typeRet = TX_CRYPTOCONDITION; vector hashBytes; uint160 x; int32_t i; uint8_t hash20[20],*ptr;; - x = Hash160(scriptPubKey); + x = Hash160(ccSubScript); memcpy(hash20,&x,20); hashBytes.resize(20); ptr = hashBytes.data(); for (i=0; i<20; i++) ptr[i] = hash20[i]; vSolutionsRet.push_back(hashBytes); + if (vParams.size()) + { + COptCCParams cp = COptCCParams(vParams[0]); + if (cp.IsValid() && vParams.size() > cp.m) + { + // all addresses that should be there must be 20 byte keyIDs + for (int i = 1; i <= cp.m; i++) + { + if (vParams[i].size() != 20) + { + // we accept no errors + return false; + } + } + } + } return true; } return false; @@ -331,6 +349,18 @@ bool ExtractDestinations(const CScript& scriptPubKey, txnouttype& typeRet, vecto if (addressRet.empty()) return false; } + else if (IsCryptoConditionsEnabled() != 0 && typeRet == TX_CRYPTOCONDITION) + { + nRequiredRet = vSolutions.front()[0]; + for (unsigned int i = 1; i < vSolutions.size()-1; i++) + { + CTxDestination address = CKeyID(uint160(vSolutions[i])); + addressRet.push_back(address); + } + + if (addressRet.empty()) + return false; + } else { nRequiredRet = 1; diff --git a/src/script/standard.h b/src/script/standard.h index 10537041e..665956bb4 100644 --- a/src/script/standard.h +++ b/src/script/standard.h @@ -83,6 +83,44 @@ public: */ typedef boost::variant CTxDestination; +class COptCCParams +{ + public: + static const uint8_t VERSION = 1; + + uint8_t version; + uint8_t evalCode; + uint8_t n, m; // for n of m sigs required, m addresses for sigs will follow + + COptCCParams() : version(0), evalCode(0), n(0), m(0) {} + + COptCCParams(uint8_t ver, uint8_t code, uint8_t _n, uint8_t _m) : version(ver), evalCode(code), n(_n), m(_m) {} + + COptCCParams(std::vector &vch) + { + version = 0; + if (vch.size() == 4) + { + version = vch[0]; + evalCode = vch[1]; + n = vch[2]; + m = vch[3]; + if (version != VERSION && n == 1 && (m == 1 || m == 2)) + { + // we only support one version, and 1 of 1 or 1 of 2 now, so set invalid + version = 0; + } + } + } + + bool IsValid() { return version != 0; } + + std::vector AsVector() + { + std::vector vch = std::vector({version, evalCode, n, m}); + } +}; + /** Check whether a CTxDestination is a CNoDestination. */ bool IsValidDestination(const CTxDestination& dest); diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index 622fd65c5..964d8c0d4 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1254,10 +1254,9 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe std::vector> vSolutions; CBlockIndex *tipindex; - { - LOCK(cs_main); - tipindex = chainActive.Tip(); - } + tipindex = chainActive.LastTip(); + bool extendedStake = tipindex->GetHeight() >= Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight; + bnTarget = lwmaGetNextPOSRequired(tipindex, Params().GetConsensus()); if (!VerusSelectStakeOutput(pBlock, hashResult, stakeSource, voutNum, tipindex->GetHeight() + 1, bnTarget) || @@ -1275,21 +1274,56 @@ int32_t CWallet::VerusStakeTransaction(CBlock *pBlock, CMutableTransaction &txNe const CKeyStore& keystore = *pwalletMain; txNew.vin.resize(1); txNew.vout.resize(1); - txfee = 0; + txfee = extendedStake ? 10000 : 0; // extended stakes will always be rebroadcast, so they require a fee to make it fast txNew.vin[0].prevout.hash = stakeSource.GetHash(); txNew.vin[0].prevout.n = voutNum; + CPubKey pk = CPubKey(); + if (whichType == TX_PUBKEY) { txNew.vout[0].scriptPubKey << ToByteVector(vSolutions[0]) << OP_CHECKSIG; + pk = CPubKey(vSolutions[0]); } else if (whichType == TX_PUBKEYHASH) { txNew.vout[0].scriptPubKey << OP_DUP << OP_HASH160 << ToByteVector(vSolutions[0]) << OP_EQUALVERIFY << OP_CHECKSIG; + if (extendedStake) + { + // we need a pubkey, so try to get one from the key ID, if not there, fail + if (!keystore.GetPubKey(CKeyID(uint160(vSolutions[0])), pk)) + return 0; + } } else return 0; + // if we are staking with the extended format, add the opreturn data required + //if (extendedStake) + { + uint256 srcBlock = uint256(); + CBlockIndex *pSrcIndex; + + txNew.vout.push_back(CTxOut()); + CTxOut &txOut1 = txNew.vout[1]; + txOut1.nValue = 0; + if (!GetTransaction(stakeSource.GetHash(), stakeSource, srcBlock)) + return 0; + + if ((pSrcIndex = mapBlockIndex[srcBlock]) == 0) + return 0; + + txOut1.scriptPubKey << OP_RETURN + << (int8_t)OPRETTYPE_STAKEPARAMS + << pSrcIndex->GetHeight() << tipindex->GetHeight() + << std::vector(pBlock->hashPrevBlock.begin(), pBlock->hashPrevBlock.end()) + << std::vector(pk.begin(), pk.end()); + + // need to decide how to decide, but then we can add a delegated source here for the coinbase output + //if (USE_EXTERNAL_PUBKEY) + // txOut1.scriptPubKey << ParseHex(NOTARY_PUBKEY); + } + nValue = txNew.vout[0].nValue = stakeSource.vout[voutNum].nValue - txfee; txNew.nLockTime = 0; CTransaction txNewConst(txNew);