From f1d392b72c168edf4c6216c29f8a5b4f9381b7b3 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 20 Feb 2019 07:10:36 -1100 Subject: [PATCH] Initial musig CC commit --- src/cc/cclib.cpp | 81 ++++++++++--- src/cc/musig.cpp | 265 +++++++++++++++++++++++++++++++++++++++++++ src/cc/rogue_rpc.cpp | 5 +- src/cc/sudoku.cpp | 4 - 4 files changed, 332 insertions(+), 23 deletions(-) create mode 100755 src/cc/musig.cpp diff --git a/src/cc/cclib.cpp b/src/cc/cclib.cpp index f2f13837c..ae3d88573 100644 --- a/src/cc/cclib.cpp +++ b/src/cc/cclib.cpp @@ -36,6 +36,7 @@ std::string MYCCLIBNAME = (char *)"rogue"; #else #define EVAL_SUDOKU 17 +#define EVAL_MUSIG 18 std::string MYCCLIBNAME = (char *)"sudoku"; #endif @@ -70,6 +71,16 @@ CClib_methods[] = { (char *)"sudoku", (char *)"txidinfo", (char *)"txid", 1, 1, 'T', EVAL_SUDOKU }, { (char *)"sudoku", (char *)"pending", (char *)"", 0, 0, 'U', EVAL_SUDOKU }, { (char *)"sudoku", (char *)"solution", (char *)"txid solution timestamps[81]", 83, 83, 'S', EVAL_SUDOKU }, + { (char *)"musig", (char *)"calcmsg", (char *)"sendtxid scriptPubKey", 2, 2, 'C', EVAL_MUSIG }, + { (char *)"musig", (char *)"combine", (char *)"pubkeys ...", 2, 256, 'P', EVAL_MUSIG }, + { (char *)"musig", (char *)"session", (char *)"msg pkhash", 2, 2, 'R', EVAL_MUSIG }, + { (char *)"musig", (char *)"commit", (char *)"pubkeys ...", 2, 256, 'H', EVAL_MUSIG }, + { (char *)"musig", (char *)"nonce", (char *)"pubkeys ...", 2, 256, 'N', EVAL_MUSIG }, + { (char *)"musig", (char *)"partialsign", (char *)"pubkeys ...", 2, 256, 'S', EVAL_MUSIG }, + { (char *)"musig", (char *)"sigcombine", (char *)"pubkeys ...", 2, 256, 'M', EVAL_MUSIG }, + { (char *)"musig", (char *)"verify", (char *)"msg sig pubkey", 3, 3, 'V', EVAL_MUSIG }, + { (char *)"musig", (char *)"send", (char *)"combined_pk amount", 2, 2, 'x', EVAL_MUSIG }, + { (char *)"musig", (char *)"spend", (char *)"sendtxid sig destpubkey", 3, 3, 'y', EVAL_MUSIG }, #endif }; @@ -98,6 +109,18 @@ UniValue sudoku_txidinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params UniValue sudoku_generate(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue sudoku_solution(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); UniValue sudoku_pending(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); + +bool musig_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx); +UniValue musig_calcmsg(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_combine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_session(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_commit(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_nonce(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_partialsign(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_sigcombine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); +UniValue musig_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params); #endif UniValue CClib_method(struct CCcontract_info *cp,char *method,cJSON *params) @@ -158,6 +181,37 @@ UniValue CClib_method(struct CCcontract_info *cp,char *method,cJSON *params) return(result); } } + else if ( cp->evalcode == EVAL_MUSIG ) + { + //printf("CClib_method params.%p\n",params); + if ( strcmp(method,"combine") == 0 ) + return(musig_combine(txfee,cp,params)); + else if ( strcmp(method,"calcmsg") == 0 ) + return(musig_calcmsg(txfee,cp,params)); + else if ( strcmp(method,"session") == 0 ) + return(musig_session(txfee,cp,params)); + else if ( strcmp(method,"commit") == 0 ) + return(musig_commit(txfee,cp,params)); + else if ( strcmp(method,"nonce") == 0 ) // returns combined nonce if ready + return(musig_nonce(txfee,cp,params)); + else if ( strcmp(method,"partialsign") == 0 ) + return(musig_partialsign(txfee,cp,params)); + else if ( strcmp(method,"sigcombine") == 0 ) + return(musig_sigcombine(txfee,cp,params)); + else if ( strcmp(method,"verify") == 0 ) + return(musig_verify(txfee,cp,params)); + else if ( strcmp(method,"send") == 0 ) + return(musig_send(txfee,cp,params)); + else if ( strcmp(method,"spend") == 0 ) + return(musig_spend(txfee,cp,params)); + else + { + result.push_back(Pair("result","error")); + result.push_back(Pair("error","invalid musig method")); + result.push_back(Pair("method",method)); + return(result); + } + } #endif else { @@ -278,7 +332,11 @@ bool CClib_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const C #ifdef BUILD_ROGUE return(rogue_validate(cp,height,eval,tx)); #else - return(sudoku_validate(cp,height,eval,tx)); + if ( cp->evalcode == EVAL_SUDOKU ) + return(sudoku_validate(cp,height,eval,tx)); + else if ( cp->evalcode == EVAL_MUSIG ) + return(musig_validate(cp,height,eval,tx)); + else return eval->Invalid("invalid evalcode"); #endif } numvins = tx.vin.size(); @@ -385,21 +443,6 @@ std::string Faucet2Fund(struct CCcontract_info *cp,uint64_t txfee,int64_t funds) return(""); } -/*UniValue FaucetInfo() -{ - UniValue result(UniValue::VOBJ); char numstr[64]; - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - CPubKey faucetpk; struct CCcontract_info *cp,C; int64_t funding; - result.push_back(Pair("result","success")); - result.push_back(Pair("name","Faucet")); - cp = CCinit(&C,EVAL_FAUCET); - faucetpk = GetUnspendable(cp,0); - funding = AddFaucetInputs(cp,mtx,faucetpk,0,0); - sprintf(numstr,"%.8f",(double)funding/COIN); - result.push_back(Pair("funding",numstr)); - return(result); -}*/ - std::string CClib_rawtxgen(struct CCcontract_info *cp,uint8_t funcid,cJSON *params) { CMutableTransaction tmpmtx,mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); @@ -481,7 +524,10 @@ cJSON *cclib_reparse(int32_t *nump,cJSON *origparams) // assumes origparams will { newstr[j++] = '"'; i += 2; - } else newstr[j++] = jsonstr[i]; + } + else if ( jsonstr[i] == ''' ) + newstr[j++] = '"'; + else newstr[j++] = jsonstr[i]; } newstr[j] = 0; params = cJSON_Parse(newstr); @@ -533,5 +579,6 @@ cJSON *cclib_reparse(int32_t *nump,cJSON *origparams) // assumes origparams will #else #include "sudoku.cpp" +#include "musig.cpp" #endif diff --git a/src/cc/musig.cpp b/src/cc/musig.cpp new file mode 100755 index 000000000..1a9d860cf --- /dev/null +++ b/src/cc/musig.cpp @@ -0,0 +1,265 @@ +/****************************************************************************** + * 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 +#include +#include + +#define MUSIG_PREVN 0 // for now, just use vout0 for the musig output + +uint256 musig_msghash(uint256 prevhash,int32_t prevn,CTxOut vout,secp256k1_pubkey combined_pk) +{ + std::vector data; uint256 hash; int32_t len = 0; + data << E_MARSHAL(ss << prevhash << prevn << vout << combined_pk); +fprintf(stderr,"data size %d\n",(int32_t)data.size()); + vcalc_sha256(0,(uint8_t *)&hash,data.ptr(),data.size()); + return(hash); +} + +uint256 musig_prevoutmsg(uint256 sendtxid,CScript scriptPubKey) +{ + CTransaction vintx; uint256 hashBlock; int32_t numvouts; CTxOut vout; secp256k1_pubkey combined_pk; + if ( myGetTransaction(prevhash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + if ( musig_sendopretdecode(combined_pk,vintx.vouts[numvouts-1].scriptPubKey) == 'x' ) + { + vout.nValue = vintx.vout[MUSIG_PREVN].nValue - txfee; + vout.scriptPubKey = scriptPubKey; + return(musig_msghash(prevhash,MUSIG_PREVN,vout,combined_pk)); + } + } + return(zeroid); +} + +UniValue musig_calcmsg(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); uint256 sendtxid,msg; char *scriptstr; int32_t n; + if ( (params= cclib_reparse(&n,params)) != 0 ) + { + if ( n == 2 ) + { + sendtxid = juint256(jitem(params,0)); + scriptstr = jstr(jitem(params,1),0); + if ( is_hexstr(scriptstr,0) != 0 ) + { + CScript scriptPubKey(ParseHex(scriptstr)); + msg = musig_prevoutmsg(sendtxid,scriptPubKey); + result.push_back("result","success"); + result.push_back("msg",msg.GetHex()); + return(result); + } else return(cclib_error(result,"script is not hex")); + } else return(cclib_error(result,"need exactly 2 parameters: sendtxid, scriptPubKey")); + } else return(cclib_error(result,"couldnt parse params")); +} + +UniValue musig_combine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back("result","success"); + return(result); +} + +UniValue musig_session(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back("result","success"); + return(result); +} + +UniValue musig_commit(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back("result","success"); + return(result); +} + +UniValue musig_nonce(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back("result","success"); + return(result); +} + +UniValue musig_partialsign(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back("result","success"); + return(result); +} + +UniValue musig_sigcombine(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back("result","success"); + return(result); +} + +UniValue musig_verify(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + UniValue result(UniValue::VOBJ); + result.push_back("result","success"); + return(result); +} + +// helpers for rpc calls that generate/validate onchain tx + +UniValue musig_rawtxresult(UniValue &result,std::string rawtx) +{ + CTransaction tx; + if ( rawtx.size() > 0 ) + { + result.push_back(Pair("hex",rawtx)); + if ( DecodeHexTx(tx,rawtx) != 0 ) + { + //if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + // RelayTransaction(tx); + result.push_back(Pair("txid",tx.GetHash().ToString())); + result.push_back(Pair("result","success")); + } else result.push_back(Pair("error","decode hex")); + } else result.push_back(Pair("error","couldnt finalize CCtx")); + return(result); +} + +CScript musig_sendopret(uint8_t funcid,secp256k1_pubkey combined_pk) +{ + CScript opret; uint8_t evalcode = EVAL_MUSIG; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << combined_pk); + return(opret); +} + +uint8_t musig_sendopretdecode(secp256k1_pubkey &combined_pk,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> combined_pk) != 0 && e == EVAL_MUSIG && f == 'x' ) + { + return(f); + } + return(0); +} + +CScript musig_spendopret(uint8_t funcid,secp256k1_pubkey combined_pk,secp256k1_schnorrsig musig) +{ + CScript opret; uint8_t evalcode = EVAL_MUSIG; + opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << combined_pk << musig); + return(opret); +} + +uint8_t musig_spendopretdecode(secp256k1_pubkey &combined_pk,secp256k1_schnorrsig &musig,CScript scriptPubKey) +{ + std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); + if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> combined_pk; ss >> musig) != 0 && e == EVAL_MUSIG && f == 'y' ) + { + return(f); + } + return(0); +} + +UniValue musig_send(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); int32_t n; char *hexstr; std::string rawtx; int64_t amount; CPubKey musigpk,mypk; + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + musigpk = GetUnspendable(cp,0); + if ( (params= cclib_reparse(&n,params)) != 0 ) + { + if ( n == 2 && (hexstr= jstr(jitem(params,0),0)) != 0 && is_hexstr(hexstr,0) == 66 ) + { + secp256k1_pubkey combined_pk(ParseHex(hexstr)); + amount = jdouble(jitem(params,1),0) * COIN + 0.0000000049; + if ( amount >= 3*txfee && AddNormalinputs(mtx,mypk,amount+2*txfee,64) >= amount+2*txfee ) + { + mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount+txfee,musigpk)); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,musig_sendopret('x',combined_pk)); + return(musig_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"couldnt find funds or less than 0.0003")); + } else return(cclib_error(result,"must have 2 params: combined_pk, amount")); + } else return(cclib_error(result,"not enough parameters")); +} + +UniValue musig_spend(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) +{ + static secp256k1_context *ctx; + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + UniValue result(UniValue::VOBJ); std::string rawtx; CPubKey mypk; secp256k1_pubkey combined_pk; char *scriptstr,*musigstr; uint256 msg,prevhash,hashBlock; int32_t n,numvouts; CTxOut vout; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( (params= cclib_reparse(&n,params)) != 0 ) + { + if ( n == 3 ) + { + prevhash = juint256(jitem(params,0)); + scriptstr = jstr(jitem(params,1),0); + musigstr = jstr(jitem(params,2),0); + if ( is_hexstr(scriptstr,0) != 0 && is_hexstr(musigstr,0) != 0 ) + { + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + secp256k1_schnorrsig musig(ParseHex(musigstr)); + CScript scriptPubKey(ParseHex(scriptstr)); + if ( myGetTransaction(prevhash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + vout.nValue = vintx.vout[0].nValue - txfee; + vout.scriptPubKey = scriptPubKey; + if ( musig_sendopretdecode(combined_pk,vintx.vouts[numvouts-1].scriptPubKey) == 'x' ) + { + msg = musig_prevoutmsg(prevhash,vout.scriptPubKey); + if ( !secp256k1_schnorrsig_verify(ctx,&musig,msg,&combined_pk) ) + return(cclib_error(result,"musig didnt validate")); + mtx.vin.push_back(CTxIn(prevhash,MUSIG_PREVN)); + mtx.vout.push_back(vout); + rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,musig_spendopret('y',combined_pk,musig)); + return(musig_rawtxresult(result,rawtx)); + } else return(cclib_error(result,"couldnt decode send opret")); + } else return(cclib_error(result,"couldnt find vin0")); + } else return(cclib_error(result,"script or musig is not hex")); + } else return(cclib_error(result,"need to have exactly 3 params prevhash, scriptPubKey, musig")); + } else return(cclib_error(result,"params parse error")); +} + +bool musig_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +{ + static secp256k1_context *ctx; + secp256k1_pubkey combined_pk,checkpk; secp256k1_schnorrsig musig; uint256 msg,hashBlock; CTransaction vintx; int32_t numvouts; + if ( ctx == 0 ) + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + if ( tx.vout.size() != 2 ) + return eval->Invalid("numvouts != 2"); + else if ( tx.vin.size() != 1 ) + return eval->Invalid("numvins != 1"); + else if ( IsCCInput(tx.vin[0].scriptSig) == 0 ) + return eval->Invalid("illegal normal vin0"); + else if ( myGetTransaction(tx.vin[0].prevout.hash,vintx,hashBlock) != 0 && (numvouts= vintx.vout.size()) > 1 ) + { + if ( musig_sendopretdecode(combined_pk,vintx.vouts[numvouts-1].scriptPubKey) == 'x' ) + { + if ( musig_spendopretdecode(check_pk,musig,tx.vout[tx.vout.size()-1].scriptPubKey) == 'y' ) + { + if ( combined_pk == check_pk ) + { + msg = musig_prevoutmsg(tx.vin[0].prevout.hash,tx.vout[0].scriptPubKey); + if ( !secp256k1_schnorrsig_verify(ctx,&musig,msg,&combined_pk) ) + return eval->Invalid("failed schnorrsig_verify"); + else return(true); + } else return eval->Invalid("combined_pk didnt match send opret"); + } else return eval->Invalid("failed decode musig spendopret"); + } else return eval->Invalid("couldnt decode send opret"); + } else return eval->Invalid("couldnt find vin0 tx"); +} diff --git a/src/cc/rogue_rpc.cpp b/src/cc/rogue_rpc.cpp index 3d89e0568..8ca239c7b 100644 --- a/src/cc/rogue_rpc.cpp +++ b/src/cc/rogue_rpc.cpp @@ -1078,7 +1078,7 @@ UniValue rogue_highlander(uint64_t txfee,struct CCcontract_info *cp,cJSON *param UniValue rogue_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; bits256 t; char myrogueaddr[64]; CPubKey mypk,roguepk; + UniValue result(UniValue::VOBJ),a(UniValue::VARR); int32_t i,n,gameheight,maxplayers,numvouts; uint256 txid; CTransaction tx; int64_t buyin; uint64_t seed; bits256 t; char myrogueaddr[64]; CPubKey mypk,roguepk; result.push_back(Pair("name","rogue")); result.push_back(Pair("method","gameinfo")); if ( (params= cclib_reparse(&n,params)) != 0 ) @@ -1095,7 +1095,8 @@ UniValue rogue_gameinfo(uint64_t txfee,struct CCcontract_info *cp,cJSON *params) roguepk = GetUnspendable(cp,0); GetCCaddress1of2(cp,myrogueaddr,roguepk,mypk); //fprintf(stderr,"myrogueaddr.%s\n",myrogueaddr); - rogue_gamefields(result,maxplayers,buyin,txid,myrogueaddr); + seed = rogue_gamefields(result,maxplayers,buyin,txid,myrogueaddr); + result.push_back(Pair("seed",(int64_t)seed)); for (i=0; iInvalid("not enough vouts"); } -#include -#include -#include -