/****************************************************************************** * Copyright © 2014-2018 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 "CCOracles.h" /* An oracles CC has the purpose of converting offchain data into onchain data simplest would just be to have a pubkey(s) that are trusted to provide such data, but this wont need to have a CC involved at all and can just be done by convention That begs the question, "what would an oracles CC do?" A couple of things come to mind, ie. payments to oracles for future offchain data and maybe some sort of dispute/censoring ability first step is to define the data that the oracle is providing. A simple name:description tx can be created to define the name and description of the oracle data. linked to this txid would be two types of transactions: a) oracle providers b) oracle data users In order to be resistant to sybil attacks, the feedback mechanism needs to have a cost. combining with the idea of payments for data, the oracle providers will be ranked by actual payments made to each oracle for each data type. Required transactions: 0) create oracle description -> just needs to create txid for oracle data 1) register as oracle data provider with price -> become a registered oracle data provider 2) pay provider for N oracle data points -> lock funds for oracle provider 3) publish oracle data point -> publish data and collect payment create: vins.*: normal inputs vout.0: txfee tag to oracle normal address vout.1: opreturn with name and description and format for data register: vins.*: normal inputs vout.0: txfee tag to normal marker address vout.1: opreturn with createtxid, pubkey and price per data point subscribe: vins.*: normal inputs vout.0: subscription fee to publishers CC address vout.1: opreturn with createtxid, registered provider's pubkey, amount data: vin.0: normal input vin.1+: subscription vout.0 vout.0: change to publishers CC address vout.1: payment for dataprovider vout.2: opreturn with data in proper format */ // start of consensus code CScript EncodeOraclesCreateOpRet(uint8_t funcid,std::string name,std::string description,std::string format) { CScript opret; uint8_t evalcode = EVAL_ORACLES; opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << name << description << format); return(opret); } uint8_t DecodeOraclesCreateOpRet(const CScript &scriptPubKey,std::string &name,std::string &description,std::string &format) { std::vector vopret; uint8_t *script,e,f,funcid; std::string name,description,format; GetOpReturnData(scriptPubKey,vopret); script = (uint8_t *)vopret.data(); if ( script[0] == EVAL_ORACLES ) { if ( script[1] == 'C' ) { if ( E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> name; ss >> description; ss >> format) != 0 ) { return(script[1]); } else fprintf(stderr,"DecodeOraclesCreateOpRet unmarshal error for C\n"); } } return(0); } CScript EncodeOraclesOpRet(uint8_t funcid,uint256 oracletxid,CPubKey pk,int64_t num) { CScript opret; uint8_t evalcode = EVAL_ORACLES; opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << oracletxid << pk << num); return(opret); } uint8_t DecodeOraclesOpRet(const CScript &scriptPubKey,uint256 &oracletxid,CPubKey &pk,int64_t &num) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey,vopret); script = (uint8_t *)vopret.data(); if ( vopret.size() > 1 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> oracletxid; ss >> pk; ss >> num) != 0 ) { if ( e == EVAL_ORACLES && (f == 'R' || f == 'S') ) return(f); } return(0); } CScript EncodeOraclesData(uint8_t funcid,uint256 oracletxid,CPubKey pk,std::vector data) { CScript opret; uint8_t evalcode = EVAL_ORACLES; opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << oracletxid << pk << data); return(opret); } uint8_t DecodeOraclesData(const CScript &scriptPubKey,uint256 &oracletxid,CPubKey &pk,std::vector &data) { std::vector vopret; uint8_t *script,e,f; GetOpReturnData(scriptPubKey,vopret); script = (uint8_t *)vopret.data(); if ( vopret.size() > 1 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> oracletxid; ss >> pk; ss >> data) != 0 ) { if ( e == EVAL_ORACLES && f == 'D' ) return(f); } return(0); } int64_t IsOraclesvout(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 OraclesExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee) { static uint256 zerohash; CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis; numvins = tx.vin.size(); numvouts = tx.vout.size(); for (i=0; iismyvin)(tx.vin[i].scriptSig) != 0 ) { //fprintf(stderr,"vini.%d check mempool\n",i); if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 ) return eval->Invalid("cant find vinTx"); else { //fprintf(stderr,"vini.%d check hash and vout\n",i); if ( hashBlock == zerohash ) return eval->Invalid("cant Oracles from mempool"); if ( (assetoshis= IsOraclesvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 ) inputs += assetoshis; } } } for (i=0; iInvalid("mismatched inputs != outputs + txfee"); } else return(true); } bool OraclesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx) { int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; return(false); std::vector > txids; numvins = tx.vin.size(); numvouts = tx.vout.size(); preventCCvins = preventCCvouts = -1; if ( numvouts < 1 ) return eval->Invalid("no vouts"); else { for (i=0; iInvalid("illegal normal vini"); } } //fprintf(stderr,"check amounts\n"); if ( OraclesExactAmounts(cp,eval,tx,1,10000) == false ) { fprintf(stderr,"Oraclesget invalid amount\n"); return false; } else { txid = tx.GetHash(); memcpy(hash,&txid,sizeof(hash)); retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); if ( retval != 0 ) fprintf(stderr,"Oraclesget validated\n"); else fprintf(stderr,"Oraclesget invalid\n"); return(retval); } } } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp int64_t AddOraclesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs) { char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; std::vector > unspentOutputs; GetCCaddress(cp,coinaddr,pk); SetCCunspents(unspentOutputs,coinaddr); for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; // no need to prevent dup if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) { if ( (nValue= IsOraclesvout(cp,vintx,vout)) > 1000000 && myIsutxo_spentinmempool(txid,vout) == 0 ) { if ( total != 0 && maxinputs != 0 ) mtx.vin.push_back(CTxIn(txid,vout,CScript())); nValue = it->second.satoshis; totalinputs += nValue; n++; if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) break; } } } return(totalinputs); } int64_t LifetimeOraclesFunds(struct CCcontract_info *cp,uint256 oracletxid,CPubKey regpk) { char coinaddr[64]; CPubKey pk; int64_t total=0,num; uint256 txid,hashBlock,subtxid; CTransaction subtx; std::vector > addressIndex; GetCCaddress(cp,coinaddr,regpk); SetCCtxids(addressIndex,coinaddr); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; if ( GetTransaction(txid,subtx,hashBlock,false) != 0 ) { if ( subtx.vout.size() > 0 && DecodeOraclesOpRet(subtx.vout[subtx.vout.size()-1].scriptPubKey,subtxid,pk,num) == 'S' && subtxid == oracletxid && regpk == pk ) { total += subtx.vout[0].nValue; } } } return(total); } int64_t OracleDatafee(uint256 oracletxid,CPubKey pk) { CTransaction oracletx; char markeraddr[64]; CPubKey markerpubkey; uint8_t buf33[33]; uint256 hashBlock; int32_t numvouts; int64_t datafee = 0; if ( GetTransaction(oracletxid,oracletx,hashBlock,false) != 0 && (numvouts= oracletx.vout.size()) > 0 ) { if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) == 'C' ) { buf33[0] = 0x02; endiancpy(&buf[1],(uint8_t *)&oracletxid,32); markerpubkey = buf2pk(buf33); Getscriptaddress(markeraddr,CScript() << markerpubkey << OP_CHECKSIG); } } return(datafee); } std::string OracleCreate(int64_t txfee,std::string name,std::string description,std::string format) { CMutableTransaction mtx; CPubKey mypk,Oraclespk; struct CCcontract_info *cp,C; cp = CCinit(&C,EVAL_ORACLES); if ( name.size() > 32 || description.size() > 4096 || format.size() > 4096 ) { fprintf(stderr,"name.%d or description.%d is too big\n",(int32_t)name.size(),(int32_t)description.size()); return(""); } if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); Oraclespk = GetUnspendable(cp,0); if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) { mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(Oraclespk)) << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesCreateOpRet('C',name,description,format))); } return(""); } std::string OracleRegister(int64_t txfee,uint256 oracletxid,int64_t datafee) { CMutableTransaction mtx; CPubKey mypk,markerpubkey; struct CCcontract_info *cp,C; uint8_t buf33[33]; char markeraddr[64]; cp = CCinit(&C,EVAL_ORACLES); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); buf33[0] = 0x02; endiancpy(&buf[1],(uint8_t *)&oracletxid,32); markerpubkey = buf2pk(buf33); Getscriptaddress(markeraddr,CScript() << markerpubkey << OP_CHECKSIG); if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 ) { mtx.vout.push_back(CTxOut(txfee,CScript() << markerpubkey << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('R',oracletxid,mypk,datafee))); } return(""); } std::string OracleSubscribe(int64_t txfee,uint256 oracletxid,CPubKey publisher,int64_t amount) { CMutableTransaction mtx; CPubKey mypk,markerpubkey; struct CCcontract_info *cp,C; uint8_t buf33[33]; char markeraddr[64]; cp = CCinit(&C,EVAL_ORACLES); if ( txfee == 0 ) txfee = 10000; mypk = pubkey2pk(Mypubkey()); buf33[0] = 0x02; endiancpy(&buf[1],(uint8_t *)&oracletxid,32); markerpubkey = buf2pk(buf33); Getscriptaddress(markeraddr,CScript() << markerpubkey << OP_CHECKSIG); if ( AddNormalinputs(mtx,mypk,amount + 2*txfee,1) > 0 ) { mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,publisher)); mtx.vout.push_back(CTxOut(txfee,CScript() << markerpubkey << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesOpRet('S',oracletxid,mypk,amount))); } return(""); } std::string OracleData(int64_t txfee,uint256 oracletxid,std::vector data) { CMutableTransaction mtx; CPubKey mypk; int64_t datafee,inputs,CCchange = 0; struct CCcontract_info *cp,C; char coinaddr[64]; cp = CCinit(&C,EVAL_ORACLES); mypk = pubkey2pk(Mypubkey()); if ( data.size() > 8192 ) { fprintf(stderr,"datasize %d is too big\n",(int32_t)data.size()); return(""); } if ( (datafee= OracleDatafee(oracletxid,mypk)) <= 0 ) { fprintf(stderr,"datafee %.8f is illegal\n",(double)datafee/COIN); return(""); } if ( txfee == 0 ) txfee = 10000; GetCCaddress(cp,coinaddr,mypk); if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 ) { if ( (inputs= AddOracleInputs(cp,mtx,mypk,oracletxid,datafee,60)) > 0 ) { if ( inputs > datafee ) CCchange = (inputs - datafee); mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); mtx.vout.push_back(CTxOut(txfee,CScript() << mypk << OP_CHECKSIG)); return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeOraclesData('D',oracletxid,mypk,data))); } } return(""); } UniValue OracleInfo(uint256 origtxid) { UniValue result(UniValue::VOBJ),a(UniValue::VARR),obj(UniValue::VOBJ); std::vector > addressIndex; CTransaction regtx; std::string name,description,format; uint256 hashBlock,txid,oracletxid; CMutableTransaction mtx; CPubKey Oraclespk,markerpubkey; struct CCcontract_info *cp,C; uint8_t buf33[33]; int64_t datafee,funding; char str[67],markeraddr[64],numstr[64]; cp = CCinit(&C,EVAL_ORACLES); Oraclespk = GetUnspendable(cp,0); buf33[0] = 0x02; endiancpy(&buf[1],(uint8_t *)&origtxid,32); markerpubkey = buf2pk(buf33); Getscriptaddress(markeraddr,CScript() << markerpubkey << OP_CHECKSIG); if ( GetTransaction(origtxid,vintx,hashBlock,false) != 0 ) { if ( vintx.vout.size() > 0 && DecodeOraclesCreateOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) { result.push_back(Pair("txid",uint256_str(str,origtxid))); result.push_back(Pair("name",name)); result.push_back(Pair("description",description)); result.push_back(Pair("marker",markeraddr)); SetCCtxids(addressIndex,markeraddr); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; if ( GetTransaction(txid,regtx,hashBlock,false) != 0 ) { if ( vintx.vout.size() > 0 && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,oracletxid,pk,datafee) == 'R' && oracletxid == origtxid ) { result.push_back(Pair("provider",pubkey33_str(str,(uint8_t *)pk.begin()))); funding = LifetimeOraclesFunds(cp,oracletxid,pk); sprintf(numstr,"%.8f",(double)funding/COIN); obj.push_back(Pair("lifetime",numstr)); funding = AddOraclesInputs(cp,mtx,pk,0,0); sprintf(numstr,"%.8f",(double)funding/COIN); obj.push_back(Pair("funds",numstr)); sprintf(numstr,"%.8f",(double)datafee/COIN); obj.push_back(Pair("datafee",numstr)); a.push_back(obj); } } } result.push_back(Pair("registered",a)); } } return(result); } UniValue OraclesList() { UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction createtx; std::string name,description,format; char str[65]; cp = CCinit(&C,EVAL_ORACLES); SetCCtxids(addressIndex,cp->normaladdr); for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) { txid = it->first.txhash; if ( GetTransaction(txid,createtx,hashBlock,false) != 0 ) { if ( createtx.vout.size() > 0 && DecodeOraclesCreateOpRet(createtx.vout[createtx.vout.size()-1].scriptPubKey,name,description,format) == 'C' ) { result.push_back(uint256_str(str,txid)); } } } return(result); }