Skip to content

Commit

Permalink
Fixes to get CC mixed mode working. Channels V2 on mixed mode. (#53)
Browse files Browse the repository at this point in the history
  • Loading branch information
Mixa84 authored and dimxy committed Dec 23, 2021
1 parent 8e58eeb commit fbbaee8
Show file tree
Hide file tree
Showing 16 changed files with 401 additions and 117 deletions.
14 changes: 8 additions & 6 deletions src/cc/CCinclude.h
Original file line number Diff line number Diff line change
Expand Up @@ -540,14 +540,14 @@ bool makeCCopret(CScript &opret, std::vector<std::vector<unsigned char>> &vData)
/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code)
/// @param pk pubkey to spend the cc
/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more
CC *MakeCCcond1(uint8_t evalcode,CPubKey pk,bool mixedMode=false);
CC *MakeCCcond1(uint8_t evalcode,CPubKey pk);

/// MakeCCcond1of2 creates new 1of2 cryptocondition that allows to spend it by either of two keys
/// @param evalcode cryptocondition eval code (transactions with this eval code in cc inputs will be forwarded to the contract associated with this eval code)
/// @param pk1 one of two pubkeys to spend the cc
/// @param pk2 second of two pubkeys to spend the cc
/// @returns cryptocondition object. Must be disposed with cc_free function when not used any more
CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2,bool mixedMode=false);
CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2);

/// GetCryptoCondition retrieves the cryptocondition from a scriptSig object
/// @param scriptSig scriptSig object with a cryptocondition
Expand Down Expand Up @@ -598,7 +598,7 @@ int32_t CClib_initcp(struct CCcontract_info *cp,uint8_t evalcode);
/// @returns true if the scriptSig object contains a cryptocondition
bool IsCCInput(CScript const& scriptSig);

bool TxHasCCEvalCode(struct CCcontract_info const *cp, const CTransaction &tx);
bool IsTxCCV2(struct CCcontract_info const *cp, const CTransaction &tx);

/// CheckTxFee checks if queried transaction fee value is not less than the actual transaction fee of a real transaction
/// @param tx transaction object which actual txfee to check
Expand Down Expand Up @@ -645,15 +645,15 @@ int64_t CCtoken_balance2(char *destaddr,uint256 tokenid);
/// @param[out] destaddr the address for the cc scriptPubKey. Should have at least 64 char buffer space
/// @param evalcode eval code for which cryptocondition will be made
/// @param pk pubkey for which cryptocondition will be made
bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk);
bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk, bool mixed=false);

/// GetCCaddress retrieves the address for the scriptPubKey for the cryptocondition that is made for eval code and public key.
/// The evalcode is taken from the cp object
/// @param cp object of CCcontract_info type
/// @param[out] destaddr the address for the cc scriptPubKey. Should have at least 64 char buffer space
/// @param pk pubkey for which cryptocondition will be made
/// @see CCcontract_info
bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk);
bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk, bool mixed=false);

/// GetCCaddress1of2 retrieves the address for the scriptPubKey for the 1of2 cryptocondition that is made for eval code and two public keys.
/// The evalcode is taken from the cp object
Expand All @@ -662,7 +662,7 @@ bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk);
/// @param pk first pubkey 1of2 cryptocondition
/// @param pk2 second pubkey of 1of2 cryptocondition
/// @see CCcontract_info
bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2);
bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2, bool mixed=false);

/// Gets adddress for token cryptocondition vout
/// @param cp CCcontract_info structure initialized with EVAL_TOKENS eval code
Expand Down Expand Up @@ -784,6 +784,8 @@ std::string FinalizeCCTx(uint64_t skipmask,struct CCcontract_info *cp,CMutableTr
/// @returns signed transaction in hex encoding
UniValue FinalizeCCTxExt(bool remote, uint64_t skipmask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret, std::vector<CPubKey> pubkeys = NULL_pubkeys);

UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret);

/// SetCCunspents returns a vector of unspent outputs on an address
/// @param[out] unspentOutputs vector of pairs of address key and amount
/// @param coinaddr address where unspent outputs are searched
Expand Down
177 changes: 177 additions & 0 deletions src/cc/CCtx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@
std::vector<CPubKey> NULL_pubkeys;
struct NSPV_CCmtxinfo NSPV_U;

#ifndef FINALIZECCTX_NO_CHANGE
#define FINALIZECCTX_NO_CHANGE 0x1
#endif
#ifndef FINALIZECCTX_NO_CHANGE_WHEN_ZERO
#define FINALIZECCTX_NO_CHANGE_WHEN_ZERO 0x2
#endif

/* see description to function definition in CCinclude.h */
bool SignTx(CMutableTransaction &mtx,int32_t vini,int64_t utxovalue,const CScript scriptPubKey)
{
Expand Down Expand Up @@ -390,6 +397,176 @@ UniValue FinalizeCCTxExt(bool remote, uint64_t CCmask, struct CCcontract_info *c
return result;
}

// extended version that supports signInfo object with conds to vins map for remote cc calls
UniValue FinalizeCCV2Tx(bool remote, uint64_t mask, struct CCcontract_info *cp, CMutableTransaction &mtx, CPubKey mypk, uint64_t txfee, CScript opret)
{
auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus());
CTransaction vintx; std::string hex; CPubKey globalpk; uint256 hashBlock; int32_t i,mgret,utxovout,n;
int64_t utxovalues[CC_MAXVINS],change,totaloutputs=0,totalinputs=0; char destaddr[64],myccaddr[64],globaladdr[64];
uint8_t *privkey = NULL, myprivkey[32] = { '\0' }; CC *cond=NULL,*vectcond = NULL;
UniValue sigData(UniValue::VARR),result(UniValue::VOBJ); const UniValue sigDataNull = NullUniValue;

globalpk = GetUnspendable(cp,0);
_GetCCaddress(myccaddr,cp->evalcode,mypk,true);
_GetCCaddress(globaladdr,cp->evalcode,globalpk,true);
n = mtx.vout.size();
for (i=0; i<n; i++)
{
totaloutputs += mtx.vout[i].nValue;
}
if ( (n= mtx.vin.size()) > CC_MAXVINS )
{
fprintf(stderr,"FinalizeCCTx: %d is too many vins\n",n);
result.push_back(Pair(JSON_HEXTX, "0"));
return result;
}
#ifdef ENABLE_WALLET
// get privkey for mypk
CKeyID keyID = mypk.GetID();
CKey vchSecret;
if (pwalletMain->GetKey(keyID, vchSecret))
memcpy(myprivkey, vchSecret.begin(), sizeof(myprivkey));
#endif
memset(utxovalues,0,sizeof(utxovalues));
for (i=0; i<n; i++)
{
if (i==0 && mtx.vin[i].prevout.n==10e8) continue;
if ( (mgret= myGetTransaction(mtx.vin[i].prevout.hash,vintx,hashBlock)) != 0 )
{
utxovout = mtx.vin[i].prevout.n;
utxovalues[i] = vintx.vout[utxovout].nValue;
totalinputs += utxovalues[i];
} else fprintf(stderr,"FinalizeCCTx couldnt find %s mgret.%d\n",mtx.vin[i].prevout.hash.ToString().c_str(),mgret);
}
if ( !(mask & FINALIZECCTX_NO_CHANGE) && totalinputs >= totaloutputs+txfee )
{
change = totalinputs - (totaloutputs+txfee);
if (!(mask & FINALIZECCTX_NO_CHANGE_WHEN_ZERO)) mtx.vout.push_back(CTxOut(change,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG));
}
if ( opret.size() > 0 )
mtx.vout.push_back(CTxOut(0,opret));
PrecomputedTransactionData txdata(mtx);
n = mtx.vin.size();
for (i=0; i<n; i++)
{
if (i==0 && mtx.vin[i].prevout.n==10e8)
continue;
if ( (mgret= myGetTransaction(mtx.vin[i].prevout.hash,vintx,hashBlock)) != 0 )
{
cond=NULL;
utxovout = mtx.vin[i].prevout.n;
if ( vintx.vout[utxovout].scriptPubKey.IsPayToCryptoCondition() == 0 )
{
if ( KOMODO_NSPV_FULLNODE )
{
if (!remote)
{
if (SignTx(mtx, i, vintx.vout[utxovout].nValue, vintx.vout[utxovout].scriptPubKey) == 0)
fprintf(stderr, "signing error for vini.%d\n",i);
}
else
{
// if no myprivkey for mypk it means remote call from nspv superlite client
// add sigData for superlite client
UniValue cc(UniValue::VNULL);
AddSigData2UniValue(sigData, i, cc, HexStr(vintx.vout[utxovout].scriptPubKey), vintx.vout[utxovout].nValue ); // store vin i with scriptPubKey
}
}
else
{
{
char addr[64];
Getscriptaddress(addr,vintx.vout[utxovout].scriptPubKey);
fprintf(stderr,"vout[%d] %.8f -> %s\n",utxovout,dstr(vintx.vout[utxovout].nValue),addr);
}
if ( NSPV_SignTx(mtx,i,vintx.vout[utxovout].nValue,vintx.vout[utxovout].scriptPubKey,0) == 0 )
fprintf(stderr,"NSPV signing error for vini.%d\n",i);
}
}
else
{
Getscriptaddress(destaddr,vintx.vout[utxovout].scriptPubKey);
if( strcmp(destaddr,globaladdr) == 0 )
{
privkey = cp->CCpriv;
cond = MakeCCcond1(cp->evalcode,globalpk);
}
else if( strcmp(destaddr,myccaddr) == 0 )
{
privkey = myprivkey;
cond = MakeCCcond1(cp->evalcode,mypk);;
}
else
{
const uint8_t nullpriv[32] = {'\0'};
// use vector of dest addresses and conds to probe vintxconds
for (auto &t : cp->CCvintxprobes) {
char coinaddr[64];
vectcond = t.CCwrapped.getCC(); // Note: need to cc_free at the function exit
if (vectcond != NULL) {
Getscriptaddress(coinaddr, CCPubKey(vectcond,true));
if (strcmp(destaddr, coinaddr) == 0) {
if (memcmp(t.CCpriv, nullpriv, sizeof(t.CCpriv) / sizeof(t.CCpriv[0])) != 0)
privkey = t.CCpriv;
else
privkey = myprivkey;
cond = vectcond;
break;
}
}
}
}
if (cond==NULL)
{
fprintf(stderr, "vini.%d has CC signing error address.(%s) %s\n", i, destaddr, EncodeHexTx(mtx).c_str());
memset(myprivkey, 0, sizeof(myprivkey));
return sigDataNull;
}
if (!remote) // we have privkey in the wallet
{
uint256 sighash = SignatureHash(CCPubKey(cond,true), mtx, i, SIGHASH_ALL,utxovalues[i],consensusBranchId, &txdata);
if (cc_signTreeSecp256k1Msg32(cond, privkey, sighash.begin()) != 0)
{
mtx.vin[i].scriptSig = CCSig(cond);
}
else
{
fprintf(stderr, "vini.%d has CC signing error address.(%s) %s\n", i, destaddr, EncodeHexTx(mtx).c_str());
memset(myprivkey, 0, sizeof(myprivkey));
return sigDataNull;
}
}
else // no privkey locally - remote call
{
// serialize cc:
UniValue ccjson;
ccjson.read(cc_conditionToJSONString(cond));
if (ccjson.empty())
{
fprintf(stderr, "vini.%d can't serialize CC.(%s) %s\n", i, destaddr, EncodeHexTx(mtx).c_str());
memset(myprivkey, 0, sizeof(myprivkey));
return sigDataNull;
}
AddSigData2UniValue(sigData, i, ccjson, std::string(), vintx.vout[utxovout].nValue); // store vin i with scriptPubKey
}
}
if (cond != NULL) cc_free(cond);
}
else fprintf(stderr,"FinalizeCCTx2 couldnt find %s mgret.%d\n",mtx.vin[i].prevout.hash.ToString().c_str(),mgret);
}
memset(myprivkey,0,sizeof(myprivkey));
for (auto &t : cp->CCvintxprobes) if (t.CCwrapped.getCC() != NULL) cc_free(t.CCwrapped.getCC());

std::string strHex = EncodeHexTx(mtx);
if ( strHex.size() > 0 )
result.push_back(Pair(JSON_HEXTX, strHex));
else {
result.push_back(Pair(JSON_HEXTX, "0"));
}
if (sigData.size() > 0) result.push_back(Pair(JSON_SIGDATA,sigData));
return result;
}

void NSPV_CCunspents(std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > &unspentOutputs,char *coinaddr,bool ccflag);
void NSPV_CCtxids(std::vector<std::pair<CAddressIndexKey, CAmount> > &txids,char *coinaddr,bool ccflag);
void NSPV_CCtxids(std::vector<uint256> &txids,char *coinaddr,bool ccflag, uint8_t evalcode,uint256 filtertxid, uint8_t func);
Expand Down
60 changes: 28 additions & 32 deletions src/cc/CCutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,36 +41,22 @@ void endiancpy(uint8_t *dest,uint8_t *src,int32_t len)
#endif
}

CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2,bool mixedMode)
CC *MakeCCcond1of2(uint8_t evalcode,CPubKey pk1,CPubKey pk2)
{
std::vector<CC*> pks; CC *Sig;
std::vector<CC*> pks;
pks.push_back(CCNewSecp256k1(pk1));
pks.push_back(CCNewSecp256k1(pk2));
CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode));
if (mixedMode)
{
CC *SigPlain = CCNewThreshold(1, pks);
Sig = cc_anon(SigPlain);
cc_free(SigPlain);
}
else
Sig = CCNewThreshold(1, pks);
CC *Sig = CCNewThreshold(1, pks);
return CCNewThreshold(2, {condCC, Sig});
}

CC *MakeCCcond1(uint8_t evalcode,CPubKey pk,bool mixedMode)
CC *MakeCCcond1(uint8_t evalcode,CPubKey pk)
{
std::vector<CC*> pks; CC *Sig;
std::vector<CC*> pks;
pks.push_back(CCNewSecp256k1(pk));
CC *condCC = CCNewEval(E_MARSHAL(ss << evalcode));
if (mixedMode)
{
CC *SigPlain = CCNewThreshold(1, pks);
Sig = cc_anon(SigPlain);
cc_free(SigPlain);
}
else
Sig = CCNewThreshold(1, pks);
CC *Sig = CCNewThreshold(1, pks);
return CCNewThreshold(2, {condCC, Sig});
}

Expand Down Expand Up @@ -151,8 +137,8 @@ CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, s
CTxOut MakeCC1voutMixed(uint8_t evalcode,CAmount nValue, CPubKey pk, std::vector<unsigned char> *vData)
{
CTxOut vout;
CC *payoutCond = MakeCCcond1(evalcode,pk,true);
vout = CTxOut(nValue,CCPubKeyMixed(payoutCond));
CC *payoutCond = MakeCCcond1(evalcode,pk);
vout = CTxOut(nValue,CCPubKey(payoutCond,true));
if ( vData )
{
vout.scriptPubKey << *vData << OP_DROP;
Expand All @@ -164,8 +150,8 @@ CTxOut MakeCC1voutMixed(uint8_t evalcode,CAmount nValue, CPubKey pk, std::vector
CTxOut MakeCC1of2voutMixed(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, std::vector<unsigned char> *vData)
{
CTxOut vout;
CC *payoutCond = MakeCCcond1of2(evalcode,pk1,pk2,true);
vout = CTxOut(nValue,CCPubKeyMixed(payoutCond));
CC *payoutCond = MakeCCcond1of2(evalcode,pk1,pk2);
vout = CTxOut(nValue,CCPubKey(payoutCond,true));
if ( vData )
{
vout.scriptPubKey << *vData << OP_DROP;
Expand Down Expand Up @@ -193,10 +179,20 @@ bool IsCCInput(CScript const& scriptSig)
return true;
}

bool TxHasCCEvalCode(struct CCcontract_info const *cp, const CTransaction &tx)
bool IsTxCCV2(struct CCcontract_info const *cp, const CTransaction &tx)
{
uint256 hashBlock; CTransaction prevtx;
for (int i=0;i<tx.vin.size();i++) if (cp->ismyvin(tx.vin[i].scriptSig)) return (true);
for (int i=0;i<tx.vout.size();i++) if (tx.vout[i].scriptPubKey.HasEvalcodeCCV2(cp->evalcode)) return (true);
return (false);
}

bool IsTxCCV2Validated(struct CCcontract_info const *cp, uint256 txid)
{
uint256 hashBlock; CTransaction tx;
if (myGetTransaction(txid,tx,hashBlock)==0) return (false);
for (int i=0;i<tx.vin.size();i++) if (cp->ismyvin(tx.vin[i].scriptSig)) return (true);
for (int i=0;i<tx.vout.size();i++) if (tx.vout[i].scriptPubKey.HasCCEvalCode(cp->evalcode)) return (true);
for (int i=0;i<tx.vout.size();i++) if (tx.vout[i].scriptPubKey.HasEvalcodeCCV2(cp->evalcode)) return (true);
return (false);
}

Expand Down Expand Up @@ -354,24 +350,24 @@ CPubKey CCCustomtxidaddr(char *txidaddr,uint256 txid,uint8_t taddr,uint8_t prefi
return(pk);
}

bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk)
bool _GetCCaddress(char *destaddr,uint8_t evalcode,CPubKey pk,bool mixed)
{
CC *payoutCond;
destaddr[0] = 0;
if ( (payoutCond= MakeCCcond1(evalcode,pk)) != 0 )
{
Getscriptaddress(destaddr,CCPubKey(payoutCond));
Getscriptaddress(destaddr,CCPubKey(payoutCond,mixed));
cc_free(payoutCond);
}
return(destaddr[0] != 0);
}

bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk)
bool GetCCaddress(struct CCcontract_info *cp,char *destaddr,CPubKey pk,bool mixed)
{
destaddr[0] = 0;
if ( pk.size() == 0 )
pk = GetUnspendable(cp,0);
return(_GetCCaddress(destaddr,cp->evalcode,pk));
return(_GetCCaddress(destaddr,cp->evalcode,pk,mixed));
}

bool _GetTokensCCaddress(char *destaddr, uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
Expand All @@ -396,13 +392,13 @@ bool GetTokensCCaddress(struct CCcontract_info *cp, char *destaddr, CPubKey pk)
}


bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2)
bool GetCCaddress1of2(struct CCcontract_info *cp,char *destaddr,CPubKey pk,CPubKey pk2, bool mixed)
{
CC *payoutCond;
destaddr[0] = 0;
if ( (payoutCond= MakeCCcond1of2(cp->evalcode,pk,pk2)) != 0 )
{
Getscriptaddress(destaddr,CCPubKey(payoutCond));
Getscriptaddress(destaddr,CCPubKey(payoutCond,mixed));
cc_free(payoutCond);
}
return(destaddr[0] != 0);
Expand Down
Loading

0 comments on commit fbbaee8

Please sign in to comment.