1 /******************************************************************************
2 * Copyright © 2014-2018 The SuperNET Developers. *
4 * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
5 * the top-level directory of this distribution for the individual copyright *
6 * holder information and the developer policies on copyright and licensing. *
8 * Unless otherwise agreed in a custom licensing agreement, no part of the *
9 * SuperNET software, including this file may be copied, modified, propagated *
10 * or distributed except according to the terms contained in the LICENSE file *
12 * Removal or modification of this copyright notice is prohibited. *
14 ******************************************************************************/
16 #include "CCGateways.h"
19 Uses MofN CC's normal msig handling to create automated deposits -> token issuing. And partial signing by the selected pubkeys for releasing the funds. A user would be able to select which pubkeys to use to construct the automated deposit/redeem multisigs.
21 the potential pubkeys to be used would be based on active oracle data providers with recent activity.
23 bind asset <-> KMD gateway deposit address
24 KMD deposit -> globally spendable marker utxo
25 spend marker utxo and spend linked/locked asset to user's CC address
27 redeem -> asset to global CC address with withdraw address -> gateway spendable marker utxo
28 spend market utxo and withdraw from gateway deposit address
33 GatewayBind coin tokenid M N pubkey(s)
34 external: deposit to depositaddr with claimpubkey
35 GatewayDeposit coin tokenid external.deposittxid -> markertxid
36 GatewayClaim coin tokenid external.deposittxid markertxid -> spend marker and deposit asset
38 GatewayWithdraw coin tokenid withdrawaddr
39 external: do withdraw to withdrawaddr and spend marker, support for partial signatures and autocomplete
41 deposit addr can be 1 to MofN pubkeys
42 1:1 gateway with native coin
44 In order to create a new gateway it is necessary to follow some strict steps.
45 1. create a token with the max possible supply that will be issued
46 2. transfer 100% of them to the gateways CC's global pubkey's asset CC address. (yes it is a bit confusing)
47 3. create an oracle with the identical name, ie. KMD and format must start with Ihh (height, blockhash, merkleroot)
48 4. register a publisher and fund it with a subscribe. there will be a special client app that will automatically publish the merkleroots.
49 5. Now a gatewaysbind can bind an external coin to an asset, along with the oracle for the merkleroots. the txid from the bind is used in most of the other gateways CC calls
52 ./c tokencreate KMD 1000000 KMD_equivalent_token_for_gatewaysCC
53 a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a
55 transfer to gateways pubkey: 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 RDMqGyREkP1Gwub1Nr5Ye8a325LGZsWBCb
56 ./c tokentransfer a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 100000000000000
57 2206fc39c0f384ca79819eb491ddbf889642cbfe4d0796bb6a8010ed53064a56
59 ./c oraclescreate KMD blockheaders Ihh
60 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808
62 ./c oraclesregister 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 1000000
63 83b59eac238cbe54616ee13b2fdde85a48ec869295eb04051671a1727c9eb402
65 ./c oraclessubscribe 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 1000
66 f9499d8bb04ffb511fcec4838d72e642ec832558824a2ce5aed87f1f686f8102
68 ./c gatewaysbind a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 KMD 100000000000000 1 1 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92
69 e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e
71 ./c gatewaysinfo e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e
75 "pubkey": "02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92",
77 "oracletxid": "1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808",
82 "tokenid": "a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a",
83 "totalsupply": "1000000.00000000",
84 "remaining": "1000000.00000000",
85 "issued": "0.00000000"
88 To make a gateway deposit, send the funds to the "deposit" address, along with any amount to the same pubkey address you want to get the assetized KMD to appear in.
90 ./komodo-cli z_sendmany "<funding addr>" '[{"address":"RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha","amount":0.0001},{"address":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj","amount":7.6999}]'
91 bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 height.1003776
93 ./komodo-cli gettxoutproof '["bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009"]'
94 04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b
96 ./komodo-cli getrawtransaction bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009
97 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b
99 gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof redeemscript amount numpks oraclepks
100 ./komodo-cli -ac_name=AT5 gatewaysdeposit e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e 1003776 KMD bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 0 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b proofhere 76a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac 7.6999 1 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92
101 -> acc58b4552c4696406b681eb853b0c9a80ef8f02e3ec0e7bbd407b9261ecba81
103 gatewaysclaim bindtxid coin deposittxid redeemscript amount
104 ./c gatewaysclaim e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD acc58b4552c4696406b681eb853b0c9a80ef8f02e3ec0e7bbd407b9261ecba81 76a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac 7.6999
107 // start of consensus code
109 CScript EncodeGatewaysBindOpRet(uint8_t funcid,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> pubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2)
111 CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
112 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << prefix << prefix2 << taddr << tokenid << totalsupply << M << N << pubkeys << oracletxid);
116 CScript EncodeGatewaysOpRet(uint8_t funcid,std::string coin,uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256>txids,int32_t height,uint256 cointxid,std::string deposithex,std::vector<uint8_t>proof,std::vector<uint8_t> redeemscript,int64_t amount)
118 CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
119 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << bindtxid << publishers << txids << height << cointxid << deposithex << proof << redeemscript << amount);
123 uint8_t DecodeGatewaysOpRet(const CScript &scriptPubKey,std::string &coin,uint256 &bindtxid,std::vector<CPubKey>&publishers,std::vector<uint256>&txids,int32_t &height,uint256 &cointxid,std::string &deposithex,std::vector<uint8_t> &proof,std::vector<uint8_t> &redeemscript,int64_t &amount)
125 std::vector<uint8_t> vopret; uint8_t *script,e,f;
126 GetOpReturnData(scriptPubKey, vopret);
127 script = (uint8_t *)vopret.data();
128 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> bindtxid; ss >> publishers; ss >> txids; ss >> height; ss >> cointxid; ss >> deposithex; ss >> proof; ss >> redeemscript; ss >> amount) != 0 )
135 uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,std::string &coin,uint256 &tokenid,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector<CPubKey> &pubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2)
137 std::vector<uint8_t> vopret; uint8_t *script,e,f;
138 GetOpReturnData(scriptPubKey, vopret);
139 script = (uint8_t *)vopret.data();
141 if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> coin; ss >> prefix; ss >> prefix2; ss >> taddr; ss >> tokenid; ss >> totalsupply; ss >> M; ss >> N; ss >> pubkeys; ss >> oracletxid) != 0 )
146 Getscriptaddress(depositaddr,GetScriptForMultisig(M,pubkeys));
147 else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(pubkeys[0])) << OP_CHECKSIG);
151 fprintf(stderr,"need to generate non-KMD addresses prefix.%d\n",prefix);
154 } else fprintf(stderr,"error decoding bind opret\n");
158 int64_t IsGatewaysvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
161 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
163 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
164 return(tx.vout[v].nValue);
169 bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
171 static uint256 zerohash;
172 CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
173 numvins = tx.vin.size();
174 numvouts = tx.vout.size();
175 for (i=0; i<numvins; i++)
177 //fprintf(stderr,"vini.%d\n",i);
178 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
180 //fprintf(stderr,"vini.%d check mempool\n",i);
181 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
182 return eval->Invalid("cant find vinTx");
185 //fprintf(stderr,"vini.%d check hash and vout\n",i);
186 if ( hashBlock == zerohash )
187 return eval->Invalid("cant Gateways from mempool");
188 if ( (assetoshis= IsGatewaysvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
189 inputs += assetoshis;
193 for (i=0; i<numvouts; i++)
195 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
196 if ( (assetoshis= IsGatewaysvout(cp,tx,i)) != 0 )
197 outputs += assetoshis;
199 if ( inputs != outputs+txfee )
201 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
202 return eval->Invalid("mismatched inputs != outputs + txfee");
207 bool GatewaysValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx)
209 int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
211 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
212 numvins = tx.vin.size();
213 numvouts = tx.vout.size();
214 preventCCvins = preventCCvouts = -1;
216 return eval->Invalid("no vouts");
219 for (i=0; i<numvins; i++)
221 if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
223 return eval->Invalid("illegal normal vini");
226 //fprintf(stderr,"check amounts\n");
227 if ( GatewaysExactAmounts(cp,eval,tx,1,10000) == false )
229 fprintf(stderr,"Gatewaysget invalid amount\n");
235 memcpy(hash,&txid,sizeof(hash));
236 retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
238 fprintf(stderr,"Gatewaysget validated\n");
239 else fprintf(stderr,"Gatewaysget invalid\n");
244 // end of consensus code
246 // helper functions for rpc calls in rpcwallet.cpp
248 int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
250 char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
251 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
252 GetCCaddress(cp,coinaddr,pk);
253 SetCCunspents(unspentOutputs,coinaddr);
254 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
256 txid = it->first.txhash;
257 vout = (int32_t)it->first.index;
258 // no need to prevent dup
259 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
261 if ( (nValue= IsGatewaysvout(cp,vintx,vout)) > 10000 && myIsutxo_spentinmempool(txid,vout) == 0 )
263 if ( total != 0 && maxinputs != 0 )
264 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
265 nValue = it->second.satoshis;
266 totalinputs += nValue;
268 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
276 UniValue GatewaysInfo(uint256 bindtxid)
278 UniValue result(UniValue::VOBJ),a(UniValue::VARR); std::string coin; 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; CMutableTransaction mtx; CPubKey Gatewayspk; struct CCcontract_info *cp,C; int32_t i; int64_t totalsupply,remaining;
279 result.push_back(Pair("result","success"));
280 result.push_back(Pair("name","Gateways"));
281 cp = CCinit(&C,EVAL_GATEWAYS);
282 Gatewayspk = GetUnspendable(cp,0);
283 _GetCCaddress(gatewaysassets,EVAL_ASSETS,Gatewayspk);
284 if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 )
287 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 > 0 )
291 result.push_back(Pair("M",M));
292 result.push_back(Pair("N",N));
294 a.push_back(pubkey33_str(str,(uint8_t *)&pubkeys[i]));
295 result.push_back(Pair("pubkeys",a));
296 } else result.push_back(Pair("pubkey",pubkey33_str(str,(uint8_t *)&pubkeys[0])));
297 result.push_back(Pair("coin",coin));
298 result.push_back(Pair("oracletxid",uint256_str(str,oracletxid)));
299 result.push_back(Pair("taddr",taddr));
300 result.push_back(Pair("prefix",prefix));
301 result.push_back(Pair("prefix2",prefix2));
302 result.push_back(Pair("deposit",depositaddr));
303 result.push_back(Pair("tokenid",uint256_str(str,tokenid)));
304 sprintf(numstr,"%.8f",(double)totalsupply/COIN);
305 result.push_back(Pair("totalsupply",numstr));
306 remaining = CCaddress_balance(gatewaysassets);
307 sprintf(numstr,"%.8f",(double)remaining/COIN);
308 result.push_back(Pair("remaining",numstr));
309 sprintf(numstr,"%.8f",(double)(totalsupply - remaining)/COIN);
310 result.push_back(Pair("issued",numstr));
316 UniValue GatewaysList()
318 UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock,oracletxid,tokenid; CTransaction vintx; std::string coin; int64_t totalsupply; char str[65],depositaddr[64]; uint8_t M,N,taddr,prefix,prefix2; std::vector<CPubKey> pubkeys;
319 cp = CCinit(&C,EVAL_GATEWAYS);
320 SetCCtxids(addressIndex,cp->unspendableCCaddr);
321 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
323 txid = it->first.txhash;
324 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
326 if ( vintx.vout.size() > 0 && DecodeGatewaysBindOpRet(depositaddr,vintx.vout[vintx.vout.size()-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 0 )
328 result.push_back(uint256_str(str,txid));
335 std::string GatewaysBind(uint64_t txfee,std::string coin,uint256 tokenid,int64_t totalsupply,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> pubkeys)
337 CMutableTransaction mtx; CTransaction oracletx; uint8_t taddr,prefix,prefix2; CPubKey mypk,gatewayspk; CScript opret; uint256 hashBlock; struct CCcontract_info *cp,C; std::string name,description,format; int32_t i,numvouts; int64_t fullsupply; char destaddr[64],coinaddr[64],str[65],*fstr;
338 cp = CCinit(&C,EVAL_GATEWAYS);
339 if ( N == 0 || N > 15 || M > N )
341 fprintf(stderr,"illegal M.%d or N.%d\n",M,N);
344 if ( strcmp((char *)"KMD",coin.c_str()) != 0 )
346 fprintf(stderr,"only KMD supported for now\n");
352 if ( pubkeys.size() != N )
354 fprintf(stderr,"M.%d N.%d but pubkeys[%d]\n",M,N,(int32_t)pubkeys.size());
359 Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pubkeys[i])) << OP_CHECKSIG);
360 if ( CCaddress_balance(coinaddr) == 0 )
362 fprintf(stderr,"M.%d N.%d but pubkeys[%d] has no balance\n",M,N,i);
368 mypk = pubkey2pk(Mypubkey());
369 gatewayspk = GetUnspendable(cp,0);
370 if ( _GetCCaddress(destaddr,EVAL_ASSETS,gatewayspk) == 0 )
372 fprintf(stderr,"Gateway bind.%s (%s) cant create globaladdr\n",coin.c_str(),uint256_str(str,tokenid));
375 if ( (fullsupply= CCfullsupply(tokenid)) != totalsupply )
377 fprintf(stderr,"Gateway bind.%s (%s) globaladdr.%s totalsupply %.8f != fullsupply %.8f\n",coin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)totalsupply/COIN,(double)fullsupply/COIN);
380 if ( CCtoken_balance(destaddr,tokenid) != totalsupply )
382 fprintf(stderr,"Gateway bind.%s (%s) globaladdr.%s token balance %.8f != %.8f\n",coin.c_str(),uint256_str(str,tokenid),cp->unspendableCCaddr,(double)CCtoken_balance(destaddr,tokenid)/COIN,(double)totalsupply/COIN);
385 if ( GetTransaction(oracletxid,oracletx,hashBlock,false) == 0 || (numvouts= oracletx.vout.size()) <= 0 )
387 fprintf(stderr,"cant find oracletxid %s\n",uint256_str(str,oracletxid));
390 if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' )
392 fprintf(stderr,"mismatched oracle name %s != %s\n",name.c_str(),coin.c_str());
395 if ( (fstr= (char *)format.c_str()) == 0 || strncmp(fstr,"Ihh",3) != 0 )
397 fprintf(stderr,"illegal format (%s) != (%s)\n",fstr,(char *)"Ihh");
400 fprintf(stderr,"implement GatewaysBindExists\n");
401 /*if ( GatewaysBindExists(cp,gatewayspk,coin,tokenid) != 0 ) // dont forget to check mempool!
403 fprintf(stderr,"Gateway bind.%s (%s) already exists\n",coin.c_str(),uint256_str(str,tokenid));
406 if ( AddNormalinputs(mtx,mypk,2*txfee,60) > 0 )
408 mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gatewayspk));
409 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2)));
411 fprintf(stderr,"cant find enough inputs\n");
415 uint256 GatewaysReverseScan(uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid)
417 CTransaction tx; uint256 hash,mhash,hashBlock,oracletxid; int64_t val; int32_t numvouts; int64_t merkleht; CPubKey pk; std::vector<uint8_t>data;
419 while ( GetTransaction(batontxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
421 if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,hash,pk,data) == 'D' && oracletxid == reforacletxid )
423 if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height )
425 if ( oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()) == sizeof(hash) &&
426 oracle_format(&mhash,&val,0,'h',(uint8_t *)data.data(),(int32_t)(sizeof(int32_t)+sizeof(uint256)),(int32_t)data.size()) == sizeof(hash) && mhash != zeroid )
430 } else return(zeroid);
438 int64_t GatewaysVerify(char *refdepositaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 cointxid,const std::string deposithex,std::vector<uint8_t>proof,uint256 merkleroot,std::vector<uint8_t>redeemscript)
440 uint256 hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; char destaddr[64],str[65]; int32_t i,numvouts; int64_t nValue = 0;
441 if ( GetTransaction(oracletxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
443 fprintf(stderr,"GatewaysVerify cant find oracletxid %s\n",uint256_str(str,oracletxid));
446 if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin )
448 fprintf(stderr,"GatewaysVerify mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str());
451 if ( DecodeHexTx(tx,deposithex) != 0 )
453 for (i=0; i<numvouts; i++)
455 Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
456 if ( strcmp(refdepositaddr,destaddr) == 0 )
458 if ( redeemscript.size() == tx.vout[claimvout].scriptPubKey.size() && memcmp(redeemscript.data(),tx.vout[claimvout].scriptPubKey.data(),redeemscript.size()) == 0 )
461 nValue = tx.vout[i].nValue;
467 for (j=0; j<redeemscript.size(); j++)
468 fprintf(stderr,"%02x",((uint8_t *)redeemscript.data())[j]);
469 fprintf(stderr," redeemscript.%d\n",(int32_t)redeemscript.size());
470 for (j=0; j<tx.vout[claimvout].scriptPubKey.size(); j++)
471 fprintf(stderr,"%02x",((uint8_t *)tx.vout[claimvout].scriptPubKey.data())[j]);
472 fprintf(stderr," claimvout.%d scriptPubKey.%d mismatch\n",claimvout,(int32_t)tx.vout[claimvout].scriptPubKey.size());
474 } else fprintf(stderr,"i.%d %s vs %s\n",i,destaddr,refdepositaddr);
477 if ( txid == cointxid )
479 fprintf(stderr,"verify proof for cointxid in merkleroot\n");
481 } else fprintf(stderr,"(%s) != (%s) or txid mismatch.%d or script mismatch\n",refdepositaddr,destaddr,txid != cointxid);
485 int64_t GatewaysDepositval(CTransaction tx)
487 int32_t numvouts,height; int64_t amount; std::string coin,deposithex; std::vector<CPubKey> publishers; std::vector<uint256>txids; uint256 bindtxid,cointxid; std::vector<uint8_t> proof; std::vector<uint8_t> claimpubkey;
488 if ( (numvouts= tx.vout.size()) > 0 )
490 if ( DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey,coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,claimpubkey,amount) == 'D' )
492 // coin, bindtxid, publishers
493 fprintf(stderr,"need to validate deposittxid more\n");
500 std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,std::vector<CPubKey>pubkeys,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vector<uint8_t>proof,std::vector<uint8_t> redeemscript,int64_t amount)
502 CMutableTransaction mtx; CTransaction bindtx; CPubKey mypk,gatewayspk; uint256 oracletxid,merkleroot,mhash,hashBlock,tokenid,txid; int64_t totalsupply; int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2; std::string coin; struct CCcontract_info *cp,C; std::vector<CPubKey> msigpubkeys,publishers; std::vector<uint256>txids; char str[65],depositaddr[64];
503 cp = CCinit(&C,EVAL_GATEWAYS);
506 mypk = pubkey2pk(Mypubkey());
507 gatewayspk = GetUnspendable(cp,0);
508 //fprintf(stderr,"GatewaysDeposit ht.%d %s %.8f numpks.%d\n",height,refcoin.c_str(),(double)amount/COIN,(int32_t)pubkeys.size());
509 if ( GetTransaction(bindtxid,bindtx,hashBlock,false) == 0 || (numvouts= bindtx.vout.size()) <= 0 )
511 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
514 if ( DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || refcoin != coin )
516 fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
519 n = (int32_t)pubkeys.size();
521 for (i=m=0; i<n; i++)
523 if ( (mhash= GatewaysReverseScan(txid,height,oracletxid,OraclesBatontxid(oracletxid,pubkeys[i]))) != zeroid )
525 if ( merkleroot == zeroid )
526 merkleroot = mhash, m = 1;
527 else if ( mhash == merkleroot )
529 publishers.push_back(pubkeys[i]);
530 txids.push_back(txid);
533 if ( 0 && (merkleroot == zeroid || m < n/2) )
535 fprintf(stderr,"couldnt find merkleroot for ht.%d %s oracle.%s m.%d vs n.%d\n",height,coin.c_str(),uint256_str(str,oracletxid),m,n);
538 if ( GatewaysVerify(depositaddr,oracletxid,claimvout,coin,cointxid,deposithex,proof,merkleroot,redeemscript) != amount )
540 fprintf(stderr,"deposittxid didnt validate\n");
543 if ( AddNormalinputs(mtx,mypk,2*txfee,60) > 0 )
545 mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk));
546 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysOpRet('D',coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,redeemscript,amount)));
548 fprintf(stderr,"cant find enough inputs\n");
552 std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,std::vector<uint8_t> redeemscript,int64_t amount)
554 CMutableTransaction mtx; CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C,*assetscp,C2; uint8_t M,N,taddr,prefix,prefix2; std::string coin; std::vector<CPubKey> msigpubkeys; int64_t totalsupply,depositamount,inputs,CCchange=0; int32_t numvouts; uint256 hashBlock,assetid,oracletxid; char str[65],depositaddr[64];
555 cp = CCinit(&C,EVAL_GATEWAYS);
556 assetscp = CCinit(&C2,EVAL_ASSETS);
559 mypk = pubkey2pk(Mypubkey());
560 gatewayspk = GetUnspendable(cp,0);
561 _GetCCaddress(cp->unspendableaddr2,EVAL_ASSETS,gatewayspk);
562 memcpy(cp->unspendablepriv2,cp->CCpriv,32);
563 cp->evalcode2 = EVAL_ASSETS;
564 if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
566 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
569 if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
571 fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
574 if ( GetTransaction(deposittxid,tx,hashBlock,false) == 0 )
576 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
579 if ( (depositamount= GatewaysDepositval(tx)) != amount )
581 fprintf(stderr,"invalid Gateways deposittxid %s %.8f != %.8f\n",uint256_str(str,deposittxid),(double)depositamount/COIN,(double)amount/COIN);
584 if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 )
586 if ( (inputs= AddAssetInputs(cp,mtx,gatewayspk,assetid,amount,60)) > 0 )
588 if ( inputs > amount )
589 CCchange = (inputs - amount);
590 mtx.vin.push_back(CTxIn(deposittxid,0,CScript()));
591 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,mypk));
593 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,gatewayspk));
594 fprintf(stderr,"finalize CCtx\n");
595 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey())));
598 fprintf(stderr,"cant find enough inputs or mismatched total\n");
602 std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector<uint8_t> withdrawpub,int64_t amount)
604 CMutableTransaction mtx; CTransaction tx; CPubKey mypk,gatewayspk; struct CCcontract_info *cp,C,*assetscp,C2; uint256 assetid,hashBlock,oracletxid; int32_t numvouts; int64_t totalsupply,inputs,CCchange=0; uint8_t M,N,taddr,prefix,prefix2; std::string coin; std::vector<CPubKey> msigpubkeys; char depositaddr[64],str[65];
605 cp = CCinit(&C,EVAL_GATEWAYS);
606 assetscp = CCinit(&C2,EVAL_ASSETS);
609 mypk = pubkey2pk(Mypubkey());
610 gatewayspk = GetUnspendable(cp,0);
611 if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
613 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
616 if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
618 fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
621 if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 )
623 if ( (inputs= AddAssetInputs(assetscp,mtx,mypk,assetid,amount,60)) > 0 )
625 if ( inputs > amount )
626 CCchange = (inputs - amount);
627 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,gatewayspk));
629 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
630 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG));
631 return(FinalizeCCTx(0,assetscp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey())));
634 fprintf(stderr,"cant find enough inputs or mismatched total\n");
638 // withdrawtxid used on external chain to create baton address, its existence in mempool (along with the withdraw) proof that the withdraw is pending