Browse Source

Check for notarized in disconnect tip

pull/4/head
jl777 6 years ago
parent
commit
f9d85c8f86
  1. 33
      src/cc/CC made easy
  2. 1
      src/cc/CCGateways.h
  3. 69
      src/cc/dapps/oraclefeed.c
  4. 62
      src/cc/gateways.cpp
  5. 9
      src/main.cpp

33
src/cc/CC made easy

@ -626,6 +626,39 @@ Conclusion
I hope this document has helped you understand what a Komodo utxo based CC contract is and how it is different from the other smart contracts. If you are now able to dive into the cc directory and start making your own CC contract, then I am very happy!
gateways CC
gateways CC is the first CC that combines multiple CC into a single one. In order to achieve its goals, both the assets CC and the oracles CC is used, in addition to a dapp that issues regular transactions. This general approach can be used to solve quite a few different use cases, so it is important to understand how a multi-CC solution is put together. There are some tricky issues that only arise when using more than one CC at a time.
Before the implementation details, first lets understand what the gateways CC does. At a high level it is similar to the old multigateway (from NXT AE 2014), but with improvements. The basic idea is to tokenize other crypto coins (like BTC) and then use the assets CC to transact/swap against the tokenized crypto. By enforcing a 1:1 peg between a specific token and BTC and an automated deposit/withdraw mechanism, it is possible to transact in the virtual BTC without the delay or expensive txfees. Then anybody that ends up having any of the BTC token would be able to withdraw actual BTC by redeeming the token.
BTC -> deposit to special address -> receive token that represents BTC onchain
do onchain transactions with the BTC token
anybody who obtains the BTC token can redeem the token and get actual BTC in the withdraw address
By bringing the operations onchain, it avoids the need for crosschain complications for each trade. The crosschain does still have to happen on the deposit and withdraw, but that is all. There is just one aspect that makes it not fully decentralized, which is the reliance on MofN multisig. However, with N trusted community members and a reasonable M value, it is not expected to be a big barrier. Since all operations are automated, the only trust needed is that M of the N multisig signers are running their nodes with the gateways dapp active and also that M of them wont collude to steal the funds locked in the multisig. In three years of operations, the MGW multigateway didnt have any incident of multisig signer misbehavior and it was only 2of3 multisig.
How can the gatewaysCC work? First, it needs a dedicated token that can be used to represent the external crypto coin. In order to avoid any issues with misplaced tokens, it is simplest to require that 100% of all the tokens are all locked in the gatewaysCC address. We want to make it so that the only way the tokens can be released from the locked address is when a verified deposit is made. So, we also need a deposit address, which means there needs to be a set of pubkeys that control the deposit address. It turns out, we also need to post merkleroot data from the external coin so that the information is onchain to be able to validate the external deposit. Since we are already trusting the deposit address signers to safekeep the external coins via MofN multisig, trusting them to post the merkleroots doesnt increase the trust footprint needed.
Now we have all the ingredients needed, a dedicated token, a set of multisig pubkeys and an oracle for merkleroots.
gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s)
With a gatewaysbind, a new gateway is defined. the pubkeys are for the custodians of the multisig funds and they also need to be posting merkleroots to the chain, so the oracle needs to be setup and funded and each of the signers needs to run the oraclefeed dapp. That posts each new merkleroot via oraclesdata and also responds to withdraw requests.
The MofN pubkeys generates a deposit address and when funds are sent to that address along with a small amount to the claim address. With the txid from this external coin, along with the txproof and the rawtransaction, all is submitted with a gatewaysdeposit. This adds a special baton output which is a gateways CC output to invoke gateways validation and also prevents double claims by using the unspent status of the baton.
gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount
gatewaysclaim bindtxid coin deposittxid destpub amount
Once the gatewaysdeposit is validated, it can be claimed and now the token is sent to the claim address. A 1:1 pegging of the external crypto to the token is established. And as long as one of the deposit address signers is running the oraclefeed, then the deposit/claim process is fully automatic and under the control of the depositor. Nothing needs to be signed by any other party! Also by using the utxo from the deposittxid, double claims are prevented.
On the withdraw side, the tokens are sent back to the address where the tokens are locked and this needs to create a redemption right that can only be used once.
gatewayswithdraw bindtxid coin withdrawpub amount
And with a bit more magic in the oraclefeed, this is achieved. To be continued...

1
src/cc/CCGateways.h

@ -27,6 +27,7 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector<uint8_t> withdrawpub,int64_t amount);
UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin);
std::string GatewaysMarkdone(uint64_t txfee,uint256 withdrawtxid,std::string refcoin,uint256 cointxid);
UniValue GatewaysMultisig(uint64_t txfee,std::string refcoin,uint256 bindtxid,uint256 withdrawtxid,char *unspentstr);
// CCcustom
UniValue GatewaysInfo(uint256 bindtxid);

69
src/cc/dapps/oraclefeed.c

@ -524,12 +524,29 @@ cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid)
}
else if ( retstr != 0 )
{
fprintf(stderr,"get_rawtransaction.(%s) error.(%s)\n",acname,retstr);
fprintf(stderr,"get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr);
free(retstr);
}
return(0);
}
void importaddress(char *refcoin,char *acname,char *depositaddr)
{
cJSON *retjson; char *retstr;
if ( (retjson= get_komodocli(refcoin,&retstr,acname,"importaddress",depositaddr,"","")) != 0 )
{
printf("importaddress.(%s)\n",jprint(retjson,0));
free_json(retjson);
}
else if ( retstr != 0 )
{
fprintf(stderr,"importaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr);
free(retstr);
}
return(0);
}
void gatewaysmarkdone(char *refcoin,char *acname,bits256 withtxid,char *coin,bits256 cointxid)
{
char str[65],str2[65],*retstr; cJSON *retjson;
@ -546,17 +563,18 @@ void gatewaysmarkdone(char *refcoin,char *acname,bits256 withtxid,char *coin,bit
}
}
int32_t get_gatewaysinfo(char *refcoin,char *acname,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr)
int32_t get_gatewaysinfo(char *refcoin,char *acname,char *depositaddr,int32_t *Mp,int32_t *Np,char *bindtxidstr,char *coin,char *oraclestr)
{
char *oracle,*retstr,*name; cJSON *retjson;
char *oracle,*retstr,*name,*deposit; cJSON *retjson;
if ( (retjson= get_komodocli(refcoin,&retstr,acname,"gatewaysinfo",bindtxidstr,"","")) != 0 )
{
if ( (oracle= jstr(retjson,"oracletxid")) != 0 && strcmp(oracle,oraclestr) == 0 )
{
if ( jstr(retjson,"coin") != 0 && strcmp(jstr(retjson,"coin"),coin) == 0 && jint(retjson,"N") >= 1 )
if ( jstr(retjson,"coin") != 0 && strcmp(jstr(retjson,"coin"),coin) == 0 && jint(retjson,"N") >= 1 && (deposit= jstr(retjson,"deposit")) != 0 )
{
*Mp = jint(retjson,"M");
*Np = jint(retjson,"N");
strcpy(depositaddr,deposit);
//printf("(%s)\n",jprint(retjson,0));
} else printf("coin.%s vs %s\n",jstr(retjson,"coin"),coin);
} else printf("%s != %s\n",oracle,oraclestr);
@ -634,7 +652,7 @@ int32_t coinaddrexists(char *refcoin,char *acname,char *coinaddr)
return(num);
}
void update_gatewayspending(char *refcoin,char *acname,char *oraclestxidstr,int32_t M,int32_t N)
void update_gatewayspending(char *refcoin,char *acname,char *bindtxidstr,int32_t M,int32_t N)
{
// check queue to prevent duplicate
// check KMD chain and mempool for txidaddr
@ -643,9 +661,9 @@ void update_gatewayspending(char *refcoin,char *acname,char *oraclestxidstr,int3
/// if enough sigs, sendrawtransaction and when it confirms spend marker (txid.2)
/// if not enough sigs, post partially signed to acname with marker2
// monitor marker2, for the partially signed withdraws
cJSON *retjson,*pending,*item; char str[65],*coinstr,*txidaddr,*signeraddr,*depositaddr,*withdrawaddr; int32_t i,j,n,retval,processed = 0; bits256 txid,cointxid,origtxid,zeroid; int64_t satoshis;
cJSON *retjson,*pending,*item,*clijson; char str[65],*unspentstr,*coinstr,*txidaddr,*signeraddr,*depositaddr,*withdrawaddr; int32_t i,j,n,retval,processed = 0; bits256 txid,cointxid,origtxid,zeroid; int64_t satoshis;
memset(&zeroid,0,sizeof(zeroid));
if ( (retjson= get_gatewayspending("KMD",acname,oraclestxidstr)) != 0 )
if ( (retjson= get_gatewayspending("KMD",acname,bindtxidstr)) != 0 )
{
if ( jint(retjson,"queueflag") != 0 && (coinstr= jstr(retjson,"coin")) != 0 && strcmp(coinstr,refcoin) == 0 )
{
@ -681,14 +699,30 @@ void update_gatewayspending(char *refcoin,char *acname,char *oraclestxidstr,int3
fprintf(stderr,"ERROR withdraw %s %s %s %.8f processed\n",refcoin,bits256_str(str,cointxid),withdrawaddr,(double)satoshis/SATOSHIDEN);
}
}
else
else if ( (unspentstr= get_listunspent(refcoin,"",depositaddr)) != 0 )
{
// check msigmarkers
// iterate for sigs depth and find the deepest
// if not already a signer, add signature and post to next
// if first one, then create a rawtx and sign it, ie. depth 1
// if fully signed, broadcast
// iterate txidaddr, find highest nValue!
if ( (clijson= get_komodocli("KMD",&retstr2,acname,"gatewaysmultisig",bindtxidstr,bits256_str(str,origtxid),unspentstr)) != 0 )
{
if ( jint(clijson,"complete") != 0 )
{
cointxid = komodobroadcast(refcoin,"",clijson2);
if ( bits256_nonz(cointxid) != 0 )
{
fprintf(stderr,"withdraw %s M.%d N.%d %s %s %.8f processed\n",refcoin,M,N,bits256_str(str,cointxid),withdrawaddr,(double)satoshis/SATOSHIDEN);
gatewaysmarkdone("KMD",acname,origtxid,refcoin,cointxid);
processed++;
}
}
else if ( jint(clijson,"partialtx") != 0 )
{
// 10000 + ith -> txidaddr
txid = komodobroadcast("KMD",acname,clijson2);
fprintf(stderr,"%s M.%d of N.%d partialtx %s sent\n",refcoin,M,N,bits256_str(str,txid));
processed++;
}
free_json(clijson);
}
free(unspentstr);
}
} else fprintf(stderr,"error sending %s txidaddr.%s -> %s exists.%d\n",acname,txidaddr,bits256_str(str,txid),coinaddrexists(refcoin,acname,txidaddr));
}
@ -762,7 +796,7 @@ oraclesdata 17a841a919c284cea8a676f34e793da002e606f19a9258a3190bed12d5aaa3ff 034
int32_t main(int32_t argc,char **argv)
{
cJSON *clijson,*clijson2,*regjson,*item; int32_t acheight,i,retval,M,N,n,height,prevheight = 0; char *format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,hexstr[4096],refcoin[64]; uint64_t price; bits256 txid;
cJSON *clijson,*clijson2,*regjson,*item; int32_t acheight,i,retval,M,N,n,height,prevheight = 0; char *format,*acname,*oraclestr,*bindtxidstr,*pkstr,*pubstr,*retstr,*retstr2,depositaddr[64],hexstr[4096],refcoin[64]; uint64_t price; bits256 txid;
if ( argc < 6 )
{
printf("usage: oraclefeed $ACNAME $ORACLETXID $MYPUBKEY $FORMAT $BINDTXID [refcoin_cli]\n");
@ -798,12 +832,13 @@ int32_t main(int32_t argc,char **argv)
printf("need to specify path to refcoin's cli as last argv\n");
exit(0);
}
if ( get_gatewaysinfo("KMD",acname,&M,&N,bindtxidstr,refcoin,oraclestr) < 0 )
if ( get_gatewaysinfo("KMD",acname,depositaddr,&M,&N,bindtxidstr,refcoin,oraclestr) < 0 )
{
printf("cant find bindtxid.(%s)\n",bindtxidstr);
exit(0);
}
printf("set refcoin <- %s [%s] M.%d of N.%d\n",refcoin,REFCOIN_CLI,M,N);
importaddress(refcoin,"",depositaddr);
printf("set refcoin %s <- %s [%s] M.%d of N.%d\n",depositaddr,refcoin,REFCOIN_CLI,M,N);
}
if ( (regjson= jarray(&n,clijson,"registered")) != 0 )
{

62
src/cc/gateways.cpp

@ -695,7 +695,7 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
}
if ( GetTransaction(deposittxid,tx,hashBlock,false) == 0 )
{
fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
fprintf(stderr,"cant find deposittxid %s\n",uint256_str(str,bindtxid));
return("");
}
if ( (depositamount= GatewaysDepositval(tx)) != amount )
@ -710,7 +710,7 @@ std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,ui
{
if ( inputs > amount )
CCchange = (inputs - amount);
mtx.vin.push_back(CTxIn(deposittxid,0,CScript()));
mtx.vin.push_back(CTxIn(deposittxid,0,CScript())); // triggers EVAL_GATEWAYS validation
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,mypk));
if ( CCchange != 0 )
mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,gatewayspk));
@ -837,3 +837,61 @@ UniValue GatewaysPendingWithdraws(uint256 bindtxid,std::string refcoin)
return(result);
}
std::string GatewaysMultisigUpdate(struct CCcontract_info *cp,int32_t &complete,int32_t &partialtx,CPubKey mypk,int32_t ith,uint256 withdrawtxid,uint8_t M,uint8_t N,char *unspentstr)
{
CMutableTransaction mtx; cJSON *unspents; std::string hex,rawtx; CScript opret; uint64_t txfee = 10000;
complete = partialtx = 0;
if ( biggestrawtx == 0 )
{
if ( (unspents= cJSON_Parse(unspentstr)) != 0 )
{
rawtx = construct_rawtx(withdrawaddr,nValue,unspents);
free_json(unspents);
}
}
{
// iterate txidaddr, extract signatures!
// iterate for sigs depth and find the deepest
// if not already a signer, add signature and post to next
// if first one, then create a rawtx and sign it, ie. depth 1
// if fully signed, broadcast
return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret));
}
return(hex);
}
UniValue GatewaysMultisig(uint64_t txfee,std::string refcoin,uint256 bindtxid,uint256 withdrawtxid,char *unspentstr)
{
UniValue result(UniValue::VOBJ); std::string coin,hex; char str[67],numstr[65],depositaddr[64],gatewaysassets[64]; uint8_t M,N; std::vector<CPubKey> pubkeys; uint8_t taddr,prefix,prefix2; uint256 tokenid,oracletxid,hashBlock; CTransaction tx; CPubKey Gatewayspk,mypk; struct CCcontract_info *cp,C; int32_t i,n,complete,partialtx; int64_t totalsupply;
cp = CCinit(&C,EVAL_GATEWAYS);
if ( txfee == 0 )
txfee = 10000;
complete = partialtx = 0;
mypk = pubkey2pk(Mypubkey());
Gatewayspk = GetUnspendable(cp,0);
_GetCCaddress(gatewaysassets,EVAL_ASSETS,Gatewayspk);
if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 )
{
depositaddr[0] = 0;
if ( tx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,tx.vout[tx.vout.size()-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 0 && M <= N && N > 1 && coin == refcoin )
{
n = pubkeys.size();
for (i=0; i<n; i++)
if ( mypk == pubkeys[i] )
break;
if ( i != n )
{
hex = GatewaysMultisigUpdate(cp,complete,partialtx,mypk,i,withdrawtxid,M,N,unspentstr);
} else fprintf(stderr,"not one of the multisig signers\n");
}
}
// complete:1 -> send tx to refcoin withdraw complete
// partialtx:1 -> send partialsig to txidaddr, satoshis 10000 + ith pubkey + 1
result.push_back(Pair("result","success"));
result.push_back(Pair("coin",refcoin));
result.push_back(Pair("complete",complete));
result.push_back(Pair("partialtx",partialtx));
result.push_back(Pair("hex",hex));
return(result);
}

9
src/main.cpp

@ -3375,6 +3375,15 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) {
CBlock block;
if (!ReadBlockFromDisk(block, pindexDelete,1))
return AbortNode(state, "Failed to read block");
{
int32_t prevMoMheightp; uint256 notarizedhash,txid;
komodo_notarized_height(&prevMoMheight,&notarizedhash,&txid);
if ( block.GetHash() == notarizedhash )
{
fprintf(stderr,"DisconnectTip trying to disconnect notarized block at ht.%d\n",(int32_t)pindexDelete->nHeight);
return(false);
}
}
// Apply the block atomically to the chain state.
uint256 anchorBeforeDisconnect = pcoinsTip->GetBestAnchor();
int64_t nStart = GetTimeMicros();

Loading…
Cancel
Save