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 prevent duplicate bindtxid and cointxid via mempool scan
20 baton from mempool for oracle
22 assets vin selector needs to filter by signable vins
28 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.
30 the potential pubkeys to be used would be based on active oracle data providers with recent activity.
32 bind asset <-> KMD gateway deposit address
33 KMD deposit -> globally spendable marker utxo
34 spend marker utxo and spend linked/locked asset to user's CC address
36 redeem -> asset to global CC address with withdraw address -> gateway spendable marker utxo
37 spend market utxo and withdraw from gateway deposit address
42 GatewayBind coin tokenid M N pubkey(s)
43 external: deposit to depositaddr with claimpubkey
44 GatewayDeposit coin tokenid external.deposittxid -> markertxid
45 GatewayClaim coin tokenid external.deposittxid markertxid -> spend marker and deposit asset
47 GatewayWithdraw coin tokenid withdrawaddr
48 external: do withdraw to withdrawaddr and spend marker, support for partial signatures and autocomplete
50 deposit addr can be 1 to MofN pubkeys
51 1:1 gateway with native coin
53 In order to create a new gateway it is necessary to follow some strict steps.
54 1. create a token with the max possible supply that will be issued
55 2. transfer 100% of them to the gateways CC's global pubkey's asset CC address. (yes it is a bit confusing)
56 3. create an oracle with the identical name, ie. KMD and format must start with Ihh (height, blockhash, merkleroot)
57 4. register a publisher and fund it with a subscribe. there will be a special client app that will automatically publish the merkleroots.
58 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
61 ./c tokencreate KMD 1000000 KMD_equivalent_token_for_gatewaysCC
62 a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a
64 transfer to gateways pubkey: 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 RDMqGyREkP1Gwub1Nr5Ye8a325LGZsWBCb
65 ./c tokentransfer a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 03ea9c062b9652d8eff34879b504eda0717895d27597aaeb60347d65eed96ccb40 100000000000000
66 2206fc39c0f384ca79819eb491ddbf889642cbfe4d0796bb6a8010ed53064a56
68 ./c oraclescreate KMD blockheaders Ihh
69 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808
71 ./c oraclesregister 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 1000000
72 83b59eac238cbe54616ee13b2fdde85a48ec869295eb04051671a1727c9eb402
74 ./c oraclessubscribe 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92 1000
75 f9499d8bb04ffb511fcec4838d72e642ec832558824a2ce5aed87f1f686f8102
77 ./c gatewaysbind a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a 1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808 KMD 100000000000000 1 1 02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92
78 e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e
80 ./c gatewaysinfo e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e
84 "pubkey": "02ebc786cb83de8dc3922ab83c21f3f8a2f3216940c3bf9da43ce39e2a3a882c92",
86 "oracletxid": "1f1aefcca2bdea8196cfd77337fb21de22d200ddea977c2f9e8742c55829d808",
91 "tokenid": "a7398a8748354dd0a3f8d07d70e65294928ecc3674674bb2d9483011ccaa9a7a",
92 "totalsupply": "1000000.00000000",
93 "remaining": "1000000.00000000",
94 "issued": "0.00000000"
97 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.
99 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 pubkey for RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha
100 ./komodo-cli z_sendmany "<funding addr>" '[{"address":"RFpxgqff7FDHFuHa3jSX5NzqqWCcELz8ha","amount":0.0001},{"address":"RHV2As4rox97BuE3LK96vMeNY8VsGRTmBj","amount":7.6999}]'
101 bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 height.1003776 merkle.90aedc2f19200afc9aca2e351438d011ebae8264a58469bf225883045f61917f
103 ./komodo-cli gettxoutproof '["bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009"]'
104 04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b
106 ./komodo-cli getrawtransaction bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009
107 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b
109 gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof destpub amount
110 ./komodo-cli -ac_name=AT5 gatewaysdeposit e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e 1003776 KMD bc41a00e429db741c3199f17546a48012fd3b7eea45dfc6bc2f5228278133009 0 010000000149964cdcd17fe9b1cae4d0f3b5f5db301d9b4f54099fdf4d34498df281757094010000006a4730440220594f3a630dd73c123f44621aa8bb9968ab86734833453dd479af6d79ae6f584202207bb5e35f13b337ccc8a88d9a006c8c5ddb016c0a6f4f2dc44357a8128623d85d01210223154bf53cd3a75e64d86697070d6437c8f0010a09c1df35b659e31ce3d79b5dffffffff0310270000000000001976a91447d2e323a14b0c3be08698aa46a9b91489b189d688ac701de52d000000001976a91459fdba29ea85c65ad90f6d38f7a6646476b26b1688acb0a86a00000000001976a914f9a9daf5519dae38b8b61d945f075da895df441d88ace18d965b 04000000232524ea04b54489eb222f8b3f566ed35504e3050488a63f0ab7b1c2030000007f91615f04835822bf6984a56482aeeb11d03814352eca9afc0a20192fdcae900000000000000000000000000000000000000000000000000000000000000000268e965ba608071d0800038e16762900000000000000000000000000000000000000000042008dd6fd4005016b4292b05df6dfd316c458c53e28fb2befb96e4870a40c6c04e733d75d8a7a18cce34fe326005efdc403bfa4644e30eeafdaeff34419edc591299e6cc5933cb2eeecbab5c4dfe97cd413b75215999a3dd02b540373581e81d8512bff1640590a6b4da4aaa9b8adc0102c38ca0022daed997b53ed192ba326e212fba5e505ce29e3ad149cef7f48d0e00948a1acd81731d84008760759211eb4abbc7b037939a7964182edb59cf9065357e864188ee5fc7316e8796963036bb99eeb9f06c95d64f78749ecec7181c12eb5d83a3b9b1c1e8a0aae9a20ce04a250b28216620bfc99bb81a6de4db80b93a5aea916de97c1a272e26644abdd683f19c5e3174a2e4513ed767d8f11a4c3074295f697839c5d9139676a813451cc7da38f68cbae5d990a79075f98903233ca04fe1b4b099e433585e5adcc45d41d54a9c648179297359c75950a5e574f13f70b728bbbf552770256315cd0a00139d6ab6934cb5ed70a4fc01a92611b096dd0028f17f4cc687b75f37dca530aa47a18321c50528dbd9272eabb3e13a87021a05918a6d2627e2caba6d7cf1a9f0b831ea3337b9a6af92746d83140078d60c72b6beacf91c9e68a34cee209e08670be1d17ff8d80b7a2285b1325461a2e33f2ee675593f1900e066a5d212615cd8da18749b0e684eee73edcc9031709be715b889c6d015cf4bd4ad5ab7e21bd3492c208930a54d353ef36a437f507ead38855633c1b88d060d9e4221ca8ce2f698e8a6ae0d41e9ace3cbd401f1e0f07650e9c126d4ef20278c8be6e85c7637513643f8d02d7ad64c09da11c16429d60e5160c345844b8158ece62794e8ad280d4e4664150e74978609ece431e51a9f9e1ce8aa49c16f36c7fd12b71acc42d893e18476b8b1e144a8175519612efc93e0aecc61f3b21212c958b0e2331d76aaa62faf11a58fe2bd91ab9ab01b906406c9bbc02df2a106e67182aae0a20b538bf19f09c57f9de5e198ba254580fb1b11e22ad526550093420cb7c68628d4c3ad329c8acc6e219093d277810ed016b6099b7e3781de412a22dacedaa2acf29e8062debcd85c7b9529a20b2782a2470763ac27cf89611a527d43ac89b8063ffb93b6ed993425194f8ee821a8493a563072c896f9584f95db28e3f2fc5fb4a6f3c39d615cd563641717cd50afb73ed3989cbf504b2043882993ce9575f56402534173b1396fbc13df80920b46788ae340ad5a91f25177cc74aa69024d76f56166199d2e4d50a053555256c4e3137ea1cee1130e916a88b6ee5cf2c85652fb8824d5dacfa485e3ef6190591ac0c2fcacc4fc7deb65aca4b0b89b76e35a46b0627e2e967cc63a5d606a984c8e63eabb98fde3e69114340ae524c974cb936e57690e98a7a74533f6f7d1d0496976496b54d14a8163efb32b70dfbb79d80a3022c4f53571c08bf044270565716b435084376714b224ab23e9817c05af8223723afc0577af5c8fc28f71036ca82528aaa4ca9bcd18a50e25d2a528f183d3a2074d968d170876d8dce434c5937261b55173ab87e03d5632ca0834fdc5387c15ab3a17d75c0f274004f289ff1bf7d14e97fdf4172eb49adfb418cc2f4794806ae7c0111c97df4d65d38679ec93fea3ef738ed565e8906a8fe1861cafe3938c772fedcfab40159938e06ef414fd299f2355c6d3369bc1bd3c4db64ce205f0a1b70a40030f505b736e28230de82e97776b5ee7b10708bb3020d28cec7a8e124549ec80c547ac4e7b52bf397c72bcfce30820554ab8fb4d1f73b209bc32a0e7e878843cdbf5f01222728ccea7e6ab7cb5e3fee3234f5b85d1985f91492f6ceaa6454a658dab5074f163ce26ed753137fa61c940679de13bd7b212cd3cf2b334f5201cecbc7473342bd7a239e09169bccd56d03000000037a9068df0625e548e71263c8361b4e904c998378f6b9e32729c3f19b10ad752e093013788222f5c26bfc5da4eeb7d32f01486a54179f19c341b79d420ea041bc8878d22fad4692b2d609c3cf190903874d3682a714c7483518c9392e07c25035010b 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999
111 -> 9d80ea79a65aaa0d464f8b762356fa01047e16e9793505a22ca04559f81a6eb6
113 gatewaysclaim bindtxid coin deposittxid destpub amount
114 ./c gatewaysclaim e6c99f79d4afb216aa8063658b4222edb773dd24bb0f8e91bd4ef341f3e47e5e KMD 9d80ea79a65aaa0d464f8b762356fa01047e16e9793505a22ca04559f81a6eb6 0223d114dededb04f253816d6ad0ce78dd08c617c94ce3c53bf50dc74a5157bef8 7.6999
117 // start of consensus code
119 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)
121 CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
122 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << prefix << prefix2 << taddr << tokenid << totalsupply << M << N << pubkeys << oracletxid);
126 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,CPubKey destpub,int64_t amount)
128 CScript opret; uint8_t evalcode = EVAL_GATEWAYS;
129 opret << OP_RETURN << E_MARSHAL(ss << evalcode << funcid << coin << bindtxid << publishers << txids << height << cointxid << deposithex << proof << destpub << amount);
133 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,CPubKey &destpub,int64_t &amount)
135 std::vector<uint8_t> vopret; uint8_t *script,e,f;
136 GetOpReturnData(scriptPubKey, vopret);
137 script = (uint8_t *)vopret.data();
138 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 >> destpub; ss >> amount) != 0 )
145 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)
147 std::vector<uint8_t> vopret; uint8_t *script,e,f;
148 GetOpReturnData(scriptPubKey, vopret);
149 script = (uint8_t *)vopret.data();
151 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 )
156 Getscriptaddress(depositaddr,GetScriptForMultisig(M,pubkeys));
157 else Getscriptaddress(depositaddr,CScript() << ParseHex(HexStr(pubkeys[0])) << OP_CHECKSIG);
161 fprintf(stderr,"need to generate non-KMD addresses prefix.%d\n",prefix);
164 } else fprintf(stderr,"error decoding bind opret\n");
168 int64_t IsGatewaysvout(struct CCcontract_info *cp,const CTransaction& tx,int32_t v)
171 if ( tx.vout[v].scriptPubKey.IsPayToCryptoCondition() != 0 )
173 if ( Getscriptaddress(destaddr,tx.vout[v].scriptPubKey) > 0 && strcmp(destaddr,cp->unspendableCCaddr) == 0 )
174 return(tx.vout[v].nValue);
179 bool GatewaysExactAmounts(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx,int32_t minage,uint64_t txfee)
181 static uint256 zerohash;
182 CTransaction vinTx; uint256 hashBlock,activehash; int32_t i,numvins,numvouts; int64_t inputs=0,outputs=0,assetoshis;
183 numvins = tx.vin.size();
184 numvouts = tx.vout.size();
185 for (i=0; i<numvins; i++)
187 //fprintf(stderr,"vini.%d\n",i);
188 if ( (*cp->ismyvin)(tx.vin[i].scriptSig) != 0 )
190 //fprintf(stderr,"vini.%d check mempool\n",i);
191 if ( eval->GetTxUnconfirmed(tx.vin[i].prevout.hash,vinTx,hashBlock) == 0 )
192 return eval->Invalid("cant find vinTx");
195 //fprintf(stderr,"vini.%d check hash and vout\n",i);
196 if ( hashBlock == zerohash )
197 return eval->Invalid("cant Gateways from mempool");
198 if ( (assetoshis= IsGatewaysvout(cp,vinTx,tx.vin[i].prevout.n)) != 0 )
199 inputs += assetoshis;
203 for (i=0; i<numvouts; i++)
205 //fprintf(stderr,"i.%d of numvouts.%d\n",i,numvouts);
206 if ( (assetoshis= IsGatewaysvout(cp,tx,i)) != 0 )
207 outputs += assetoshis;
209 if ( inputs != outputs+txfee )
211 fprintf(stderr,"inputs %llu vs outputs %llu\n",(long long)inputs,(long long)outputs);
212 return eval->Invalid("mismatched inputs != outputs + txfee");
217 bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx)
219 int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64];
220 std::vector<std::pair<CAddressIndexKey, CAmount> > txids;
221 fprintf(stderr,"return true without gateways validation\n");
223 numvins = tx.vin.size();
224 numvouts = tx.vout.size();
225 preventCCvins = preventCCvouts = -1;
227 return eval->Invalid("no vouts");
230 for (i=0; i<numvins; i++)
232 if ( IsCCInput(tx.vin[0].scriptSig) == 0 )
234 return eval->Invalid("illegal normal vini");
237 //fprintf(stderr,"check amounts\n");
238 if ( GatewaysExactAmounts(cp,eval,tx,1,10000) == false )
240 fprintf(stderr,"Gatewaysget invalid amount\n");
246 memcpy(hash,&txid,sizeof(hash));
247 retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts);
249 fprintf(stderr,"Gatewaysget validated\n");
250 else fprintf(stderr,"Gatewaysget invalid\n");
255 // end of consensus code
257 // helper functions for rpc calls in rpcwallet.cpp
259 int64_t AddGatewaysInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk,int64_t total,int32_t maxinputs)
261 char coinaddr[64]; int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector<uint8_t> origpubkey; CTransaction vintx; int32_t vout,n = 0;
262 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
263 GetCCaddress(cp,coinaddr,pk);
264 SetCCunspents(unspentOutputs,coinaddr);
265 for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
267 txid = it->first.txhash;
268 vout = (int32_t)it->first.index;
269 // no need to prevent dup
270 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
272 if ( (nValue= IsGatewaysvout(cp,vintx,vout)) > 10000 && myIsutxo_spentinmempool(txid,vout) == 0 )
274 if ( total != 0 && maxinputs != 0 )
275 mtx.vin.push_back(CTxIn(txid,vout,CScript()));
276 nValue = it->second.satoshis;
277 totalinputs += nValue;
279 if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) )
287 int32_t GatewaysBindExists(struct CCcontract_info *cp,CPubKey gatewayspk,uint256 reftokenid) // dont forget to check mempool!
289 char markeraddr[64],depositaddr[64]; std::string coin; int32_t numvouts; int64_t totalsupply; uint256 tokenid,oracletxid,hashBlock; uint8_t M,N,taddr,prefix,prefix2; std::vector<CPubKey> pubkeys; CTransaction tx;
290 std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
291 _GetCCaddress(markeraddr,EVAL_GATEWAYS,gatewayspk);
292 fprintf(stderr,"bind markeraddr.(%s) need to scan mempool also\n",markeraddr);
293 SetCCtxids(addressIndex,markeraddr);
294 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
296 if ( GetTransaction(it->first.txhash,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
298 if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) == 'B' )
300 if ( tokenid == reftokenid )
302 fprintf(stderr,"trying to bind an existing tokenid\n");
311 int32_t GatewaysCointxidExists(struct CCcontract_info *cp,uint256 cointxid) // dont forget to check mempool!
313 char txidaddr[64]; std::string coin; int32_t numvouts; uint256 hashBlock;
314 std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex;
315 CCtxidaddr(txidaddr,cointxid);
316 fprintf(stderr," txidaddr.(%s) need to scan mempool also\n",txidaddr);
317 SetCCtxids(addressIndex,txidaddr);
318 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
325 UniValue GatewaysInfo(uint256 bindtxid)
327 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;
328 result.push_back(Pair("result","success"));
329 result.push_back(Pair("name","Gateways"));
330 cp = CCinit(&C,EVAL_GATEWAYS);
331 Gatewayspk = GetUnspendable(cp,0);
332 _GetCCaddress(gatewaysassets,EVAL_ASSETS,Gatewayspk);
333 if ( GetTransaction(bindtxid,tx,hashBlock,false) != 0 )
336 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 )
340 result.push_back(Pair("M",M));
341 result.push_back(Pair("N",N));
343 a.push_back(pubkey33_str(str,(uint8_t *)&pubkeys[i]));
344 result.push_back(Pair("pubkeys",a));
345 } else result.push_back(Pair("pubkey",pubkey33_str(str,(uint8_t *)&pubkeys[0])));
346 result.push_back(Pair("coin",coin));
347 result.push_back(Pair("oracletxid",uint256_str(str,oracletxid)));
348 result.push_back(Pair("taddr",taddr));
349 result.push_back(Pair("prefix",prefix));
350 result.push_back(Pair("prefix2",prefix2));
351 result.push_back(Pair("deposit",depositaddr));
352 result.push_back(Pair("tokenid",uint256_str(str,tokenid)));
353 sprintf(numstr,"%.8f",(double)totalsupply/COIN);
354 result.push_back(Pair("totalsupply",numstr));
355 remaining = CCaddress_balance(gatewaysassets);
356 sprintf(numstr,"%.8f",(double)remaining/COIN);
357 result.push_back(Pair("remaining",numstr));
358 sprintf(numstr,"%.8f",(double)(totalsupply - remaining)/COIN);
359 result.push_back(Pair("issued",numstr));
365 UniValue GatewaysList()
367 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;
368 cp = CCinit(&C,EVAL_GATEWAYS);
369 SetCCtxids(addressIndex,cp->unspendableCCaddr);
370 for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
372 txid = it->first.txhash;
373 if ( GetTransaction(txid,vintx,hashBlock,false) != 0 )
375 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 )
377 result.push_back(uint256_str(str,txid));
384 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)
386 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;
387 cp = CCinit(&C,EVAL_GATEWAYS);
388 if ( N == 0 || N > 15 || M > N )
390 fprintf(stderr,"illegal M.%d or N.%d\n",M,N);
393 if ( strcmp((char *)"KMD",coin.c_str()) != 0 )
395 fprintf(stderr,"only KMD supported for now\n");
401 if ( pubkeys.size() != N )
403 fprintf(stderr,"M.%d N.%d but pubkeys[%d]\n",M,N,(int32_t)pubkeys.size());
408 Getscriptaddress(coinaddr,CScript() << ParseHex(HexStr(pubkeys[i])) << OP_CHECKSIG);
409 if ( CCaddress_balance(coinaddr) == 0 )
411 fprintf(stderr,"M.%d N.%d but pubkeys[%d] has no balance\n",M,N,i);
417 mypk = pubkey2pk(Mypubkey());
418 gatewayspk = GetUnspendable(cp,0);
419 if ( _GetCCaddress(destaddr,EVAL_ASSETS,gatewayspk) == 0 )
421 fprintf(stderr,"Gateway bind.%s (%s) cant create globaladdr\n",coin.c_str(),uint256_str(str,tokenid));
424 if ( (fullsupply= CCfullsupply(tokenid)) != totalsupply )
426 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);
429 if ( CCtoken_balance(destaddr,tokenid) != totalsupply )
431 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);
434 if ( GetTransaction(oracletxid,oracletx,hashBlock,false) == 0 || (numvouts= oracletx.vout.size()) <= 0 )
436 fprintf(stderr,"cant find oracletxid %s\n",uint256_str(str,oracletxid));
439 if ( DecodeOraclesCreateOpRet(oracletx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' )
441 fprintf(stderr,"mismatched oracle name %s != %s\n",name.c_str(),coin.c_str());
444 if ( (fstr= (char *)format.c_str()) == 0 || strncmp(fstr,"Ihh",3) != 0 )
446 fprintf(stderr,"illegal format (%s) != (%s)\n",fstr,(char *)"Ihh");
449 if ( GatewaysBindExists(cp,gatewayspk,tokenid) != 0 ) // dont forget to check mempool!
451 fprintf(stderr,"Gateway bind.%s (%s) already exists\n",coin.c_str(),uint256_str(str,tokenid));
454 if ( AddNormalinputs(mtx,mypk,2*txfee,60) > 0 )
456 mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,gatewayspk));
457 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysBindOpRet('B',coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2)));
459 fprintf(stderr,"cant find enough inputs\n");
463 uint256 GatewaysReverseScan(uint256 &txid,int32_t height,uint256 reforacletxid,uint256 batontxid)
465 CTransaction tx; uint256 hash,mhash,hashBlock,oracletxid; int64_t val; int32_t numvouts; int64_t merkleht; CPubKey pk; std::vector<uint8_t>data;
467 while ( GetTransaction(batontxid,tx,hashBlock,false) != 0 && (numvouts= tx.vout.size()) > 0 )
469 if ( DecodeOraclesData(tx.vout[numvouts-1].scriptPubKey,oracletxid,hash,pk,data) == 'D' && oracletxid == reforacletxid )
471 if ( oracle_format(&hash,&merkleht,0,'I',(uint8_t *)data.data(),0,(int32_t)data.size()) == sizeof(int32_t) && merkleht == height )
473 if ( oracle_format(&hash,&val,0,'h',(uint8_t *)data.data(),sizeof(int32_t),(int32_t)data.size()) == sizeof(hash) &&
474 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 )
478 } else return(zeroid);
486 /* Get the block merkle root for a proof
489 * OUT: transaction IDS
491 uint256 BitcoinGetProofMerkleRoot(const std::vector<uint8_t> &proofData, std::vector<uint256> &txids)
493 CMerkleBlock merkleBlock;
494 if (!E_UNMARSHAL(proofData, ss >> merkleBlock))
496 return merkleBlock.txn.ExtractMatches(txids);
499 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,CPubKey destpub)
501 std::vector<uint256> txids; uint256 proofroot,hashBlock,txid = zeroid; CTransaction tx; std::string name,description,format; char destaddr[64],destpubaddr[64],claimaddr[64],str[65],str2[65]; int32_t i,numvouts; int64_t nValue = 0;
502 if ( GetTransaction(oracletxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
504 fprintf(stderr,"GatewaysVerify cant find oracletxid %s\n",uint256_str(str,oracletxid));
507 if ( DecodeOraclesCreateOpRet(tx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' || name != refcoin )
509 fprintf(stderr,"GatewaysVerify mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str());
512 proofroot = BitcoinGetProofMerkleRoot(proof,txids);
513 if ( proofroot != merkleroot )
515 fprintf(stderr,"GatewaysVerify mismatched merkleroot %s != %s\n",uint256_str(str,proofroot),uint256_str(str2,merkleroot));
518 if ( DecodeHexTx(tx,deposithex) != 0 )
520 Getscriptaddress(claimaddr,tx.vout[claimvout].scriptPubKey);
521 Getscriptaddress(destpubaddr,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
522 if ( strcmp(claimaddr,destpubaddr) == 0 )
524 for (i=0; i<numvouts; i++)
526 Getscriptaddress(destaddr,tx.vout[i].scriptPubKey);
527 if ( strcmp(refdepositaddr,destaddr) == 0 )
530 nValue = tx.vout[i].nValue;
534 } else fprintf(stderr,"claimaddr.(%s) != destpubaddr.(%s)\n",claimaddr,destpubaddr);
536 if ( txid == cointxid )
538 fprintf(stderr,"verify proof for cointxid in merkleroot\n");
540 } else fprintf(stderr,"(%s) != (%s) or txid mismatch.%d or script mismatch\n",refdepositaddr,destaddr,txid != cointxid);
544 int64_t GatewaysDepositval(CTransaction tx)
546 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; CPubKey claimpubkey;
547 if ( (numvouts= tx.vout.size()) > 0 )
549 if ( DecodeGatewaysOpRet(tx.vout[numvouts-1].scriptPubKey,coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,claimpubkey,amount) == 'D' )
551 // coin, bindtxid, publishers
552 fprintf(stderr,"need to validate deposittxid more\n");
559 std::string GatewaysDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 cointxid,int32_t claimvout,std::string deposithex,std::vector<uint8_t>proof,CPubKey destpub,int64_t amount)
561 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> pubkeys,publishers; std::vector<uint256>txids; char str[65],depositaddr[64],txidaddr[64];
562 cp = CCinit(&C,EVAL_GATEWAYS);
565 mypk = pubkey2pk(Mypubkey());
566 gatewayspk = GetUnspendable(cp,0);
567 //fprintf(stderr,"GatewaysDeposit ht.%d %s %.8f numpks.%d\n",height,refcoin.c_str(),(double)amount/COIN,(int32_t)pubkeys.size());
568 if ( GetTransaction(bindtxid,bindtx,hashBlock,false) == 0 || (numvouts= bindtx.vout.size()) <= 0 )
570 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
573 if ( DecodeGatewaysBindOpRet(depositaddr,bindtx.vout[numvouts-1].scriptPubKey,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys,taddr,prefix,prefix2) != 'B' || refcoin != coin )
575 fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
578 if ( GatewaysCointxidExists(cp,cointxid) != 0 )
580 fprintf(stderr,"cointxid.%s already exists\n",uint256_str(str,cointxid));
583 n = (int32_t)pubkeys.size();
585 for (i=m=0; i<n; i++)
587 if ( (mhash= GatewaysReverseScan(txid,height,oracletxid,OraclesBatontxid(oracletxid,pubkeys[i]))) != zeroid )
589 if ( merkleroot == zeroid )
590 merkleroot = mhash, m = 1;
591 else if ( mhash == merkleroot )
593 publishers.push_back(pubkeys[i]);
594 txids.push_back(txid);
597 if ( merkleroot == zeroid || m < n/2 )
600 decode_hex((uint8_t *)&tmp,32,(char *)"90aedc2f19200afc9aca2e351438d011ebae8264a58469bf225883045f61917f");
601 merkleroot = revuint256(tmp);
602 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);
605 if ( GatewaysVerify(depositaddr,oracletxid,claimvout,coin,cointxid,deposithex,proof,merkleroot,destpub) != amount )
607 fprintf(stderr,"deposittxid didnt validate\n");
610 if ( AddNormalinputs(mtx,mypk,3*txfee,60) > 0 )
612 mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk));
613 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(CCtxidaddr(txidaddr,cointxid))) << OP_CHECKSIG));
614 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeGatewaysOpRet('D',coin,bindtxid,publishers,txids,height,cointxid,deposithex,proof,destpub,amount)));
616 fprintf(stderr,"cant find enough inputs\n");
620 std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string refcoin,uint256 deposittxid,CPubKey destpub,int64_t amount)
622 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];
623 cp = CCinit(&C,EVAL_GATEWAYS);
624 assetscp = CCinit(&C2,EVAL_ASSETS);
627 mypk = pubkey2pk(Mypubkey());
628 gatewayspk = GetUnspendable(cp,0);
629 _GetCCaddress(cp->unspendableaddr2,EVAL_ASSETS,gatewayspk);
630 memcpy(cp->unspendablepriv2,cp->CCpriv,32);
631 assetscp->evalcode2 = cp->evalcode2 = EVAL_ASSETS;
632 assetscp->unspendablepk2 = gatewayspk;
633 cp->unspendablepk2 = gatewayspk;
634 memcpy(assetscp->unspendablepriv2,cp->CCpriv,32);
635 strcpy(assetscp->unspendableaddr2,cp->unspendableaddr2);
636 if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
638 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
641 if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
643 fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
646 if ( GetTransaction(deposittxid,tx,hashBlock,false) == 0 )
648 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
651 if ( (depositamount= GatewaysDepositval(tx)) != amount )
653 fprintf(stderr,"invalid Gateways deposittxid %s %.8f != %.8f\n",uint256_str(str,deposittxid),(double)depositamount/COIN,(double)amount/COIN);
656 //fprintf(stderr,"depositaddr.(%s) vs %s\n",depositaddr,cp->unspendableaddr2);
657 if ( AddNormalinputs(mtx,mypk,txfee,1) > 0 )
659 if ( (inputs= AddAssetInputs(cp,mtx,gatewayspk,assetid,amount,60)) > 0 )
661 if ( inputs > amount )
662 CCchange = (inputs - amount);
663 mtx.vin.push_back(CTxIn(deposittxid,0,CScript()));
664 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,mypk));
666 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,gatewayspk));
667 return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey())));
670 fprintf(stderr,"cant find enough inputs or mismatched total\n");
674 std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector<uint8_t> withdrawpub,int64_t amount)
676 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];
677 cp = CCinit(&C,EVAL_GATEWAYS);
678 assetscp = CCinit(&C2,EVAL_ASSETS);
681 mypk = pubkey2pk(Mypubkey());
682 gatewayspk = GetUnspendable(cp,0);
683 if ( GetTransaction(bindtxid,tx,hashBlock,false) == 0 || (numvouts= tx.vout.size()) <= 0 )
685 fprintf(stderr,"cant find bindtxid %s\n",uint256_str(str,bindtxid));
688 if ( DecodeGatewaysBindOpRet(depositaddr,tx.vout[numvouts-1].scriptPubKey,coin,assetid,totalsupply,oracletxid,M,N,msigpubkeys,taddr,prefix,prefix2) != 'B' || coin != refcoin )
690 fprintf(stderr,"invalid bindtxid %s coin.%s\n",uint256_str(str,bindtxid),coin.c_str());
693 if ( AddNormalinputs(mtx,mypk,2*txfee,1) > 0 )
695 if ( (inputs= AddAssetInputs(assetscp,mtx,mypk,assetid,amount,60)) > 0 )
697 if ( inputs > amount )
698 CCchange = (inputs - amount);
699 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,amount,gatewayspk));
701 mtx.vout.push_back(MakeCC1vout(EVAL_ASSETS,CCchange,mypk));
702 mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(withdrawpub)) << OP_CHECKSIG));
703 return(FinalizeCCTx(0,assetscp,mtx,mypk,txfee,EncodeAssetOpRet('t',assetid,zeroid,0,Mypubkey())));
706 fprintf(stderr,"cant find enough inputs or mismatched total\n");
710 // withdrawtxid used on external chain to create baton address, its existence in mempool (along with the withdraw) proof that the withdraw is pending