]> Git Repo - VerusCoin.git/blob - src/pbaas/crosschainrpc.cpp
ID registration fee fix for testnet
[VerusCoin.git] / src / pbaas / crosschainrpc.cpp
1 /********************************************************************
2  * (C) 2019 Michael Toutonghi
3  * 
4  * Distributed under the MIT software license, see the accompanying
5  * file COPYING or http://www.opensource.org/licenses/mit-license.php.
6  * 
7  * This provides support for PBaaS cross chain communication.
8  * 
9  * In merge mining and notarization, Verus acts as a hub that other PBaaS chains
10  * call via RPC in order to get information that allows earning and submitting
11  * notarizations.
12  * 
13  * All PBaaS chains communicate with their primary reserve chain, which is either Verus
14  * or the chain that is their reserve coin. The child PBaaS chain initiates all of
15  * the communication with the parent / reserve daemon.
16  * 
17  * Generally, the PBaaS chain will call the Verus chain to either get information needed
18  * to create an earned or accepted notarization. If there is no Verus daemon available
19  * staking and mining of a PBaaS chain proceeds as usual, but without notarization
20  * reward opportunities.
21  * 
22  */
23
24 #include "chainparamsbase.h"
25 #include "clientversion.h"
26 #include "rpc/client.h"
27 #include "rpc/protocol.h"
28 #include "util.h"
29 #include "utilstrencodings.h"
30
31 #include <boost/filesystem/operations.hpp>
32 #include <boost/format.hpp>
33 #include <stdio.h>
34
35 #include <event2/buffer.h>
36 #include <event2/keyvalq_struct.h>
37 #include "support/events.h"
38
39 #include <univalue.h>
40
41 #include "uint256.h"
42 #include "hash.h"
43 #include "pbaas/crosschainrpc.h"
44 #include "pbaas/identity.h"
45
46 using namespace std;
47
48 extern string PBAAS_HOST;
49 extern string PBAAS_USERPASS;
50 extern int32_t PBAAS_PORT;
51 extern std::string VERUS_CHAINNAME;
52
53 //
54 // Exception thrown on connection error.  This error is used to determine
55 // when to wait if -rpcwait is given.
56 //
57 class CConnectionFailed : public std::runtime_error
58 {
59 public:
60
61     explicit inline CConnectionFailed(const std::string& msg) :
62         std::runtime_error(msg)
63     {}
64
65 };
66
67 /** Reply structure for request_done to fill in */
68 struct HTTPReply
69 {
70     HTTPReply(): status(0), error(-1) {}
71
72     int status;
73     int error;
74     std::string body;
75 };
76
77 const char *http_errorstring(int code)
78 {
79     switch(code) {
80 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
81     case EVREQ_HTTP_TIMEOUT:
82         return "timeout reached";
83     case EVREQ_HTTP_EOF:
84         return "EOF reached";
85     case EVREQ_HTTP_INVALID_HEADER:
86         return "error while reading header, or invalid header";
87     case EVREQ_HTTP_BUFFER_ERROR:
88         return "error encountered while reading or writing";
89     case EVREQ_HTTP_REQUEST_CANCEL:
90         return "request was canceled";
91     case EVREQ_HTTP_DATA_TOO_LONG:
92         return "response body is larger than allowed";
93 #endif
94     default:
95         return "unknown";
96     }
97 }
98
99 static void http_request_done(struct evhttp_request *req, void *ctx)
100 {
101     HTTPReply *reply = static_cast<HTTPReply*>(ctx);
102
103     if (req == NULL) {
104         /* If req is NULL, it means an error occurred while connecting: the
105          * error code will have been passed to http_error_cb.
106          */
107         reply->status = 0;
108         return;
109     }
110
111     reply->status = evhttp_request_get_response_code(req);
112
113     struct evbuffer *buf = evhttp_request_get_input_buffer(req);
114     if (buf)
115     {
116         size_t size = evbuffer_get_length(buf);
117         const char *data = (const char*)evbuffer_pullup(buf, size);
118         if (data)
119             reply->body = std::string(data, size);
120         evbuffer_drain(buf, size);
121     }
122 }
123
124 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
125 static void http_error_cb(enum evhttp_request_error err, void *ctx)
126 {
127     HTTPReply *reply = static_cast<HTTPReply*>(ctx);
128     reply->error = err;
129 }
130 #endif
131
132 static CCrossChainRPCData LoadFromConfig(std::string name)
133 {
134     map<string, string> settings;
135     map<string, vector<string>> settingsmulti;
136     CCrossChainRPCData ret;
137
138     // if we are requested to automatically load the information from the Verus chain, do it if we can find the daemon
139     if (ReadConfigFile(name, settings, settingsmulti))
140     {
141         auto rpcuser = settings.find("-rpcuser");
142         auto rpcpwd = settings.find("-rpcpassword");
143         auto rpcport = settings.find("-rpcport");
144         auto rpchost = settings.find("-rpchost");
145         ret.credentials = rpcuser != settings.end() ? rpcuser->second + ":" : "";
146         ret.credentials += rpcpwd != settings.end() ? rpcpwd->second : "";
147         ret.port = rpcport != settings.end() ? atoi(rpcport->second) : (name == "VRSC" ? 27486 : 0);
148         ret.host = rpchost != settings.end() ? rpchost->second : "127.0.0.1";
149     }
150     return ret;
151 }
152
153 // credentials for now are "user:password"
154 UniValue RPCCall(const string& strMethod, const UniValue& params, const string credentials, int port, const string host, int timeout)
155 {
156     // Used for inter-daemon communicatoin to enable merge mining and notarization without a client
157     //
158
159     // Obtain event base
160     raii_event_base base = obtain_event_base();
161
162     // Synchronously look up hostname
163     raii_evhttp_connection evcon = obtain_evhttp_connection_base(base.get(), host, port);
164     evhttp_connection_set_timeout(evcon.get(), timeout);
165
166     HTTPReply response;
167     raii_evhttp_request req = obtain_evhttp_request(http_request_done, (void*)&response);
168     if (req == NULL)
169         throw std::runtime_error("create http request failed");
170 #if LIBEVENT_VERSION_NUMBER >= 0x02010300
171     evhttp_request_set_error_cb(req.get(), http_error_cb);
172 #endif
173
174     struct evkeyvalq* output_headers = evhttp_request_get_output_headers(req.get());
175     assert(output_headers);
176     evhttp_add_header(output_headers, "Host", host.c_str());
177     evhttp_add_header(output_headers, "Connection", "close");
178     evhttp_add_header(output_headers, "Authorization", (std::string("Basic ") + EncodeBase64(credentials)).c_str());
179
180     // Attach request data
181     std::string strRequest = JSONRPCRequest(strMethod, params, 1);
182     struct evbuffer* output_buffer = evhttp_request_get_output_buffer(req.get());
183     assert(output_buffer);
184     evbuffer_add(output_buffer, strRequest.data(), strRequest.size());
185
186     int r = evhttp_make_request(evcon.get(), req.get(), EVHTTP_REQ_POST, "/");
187     req.release(); // ownership moved to evcon in above call
188     if (r != 0) {
189         throw CConnectionFailed("send http request failed");
190     }
191
192     event_base_dispatch(base.get());
193
194     if (response.status == 0)
195         throw CConnectionFailed(strprintf("couldn't connect to server: %s (code %d)\n(make sure server is running and you are connecting to the correct RPC port)", http_errorstring(response.error), response.error));
196     else if (response.status == HTTP_UNAUTHORIZED)
197         throw std::runtime_error("incorrect rpcuser or rpcpassword (authorization failed)");
198     else if (response.status >= 400 && response.status != HTTP_BAD_REQUEST && response.status != HTTP_NOT_FOUND && response.status != HTTP_INTERNAL_SERVER_ERROR)
199         throw std::runtime_error(strprintf("server returned HTTP error %d", response.status));
200     else if (response.body.empty())
201         throw std::runtime_error("no response from server");
202
203     // Parse reply
204     UniValue valReply(UniValue::VSTR);
205     if (!valReply.read(response.body))
206         throw std::runtime_error("couldn't parse reply from server");
207     const UniValue& reply = valReply.get_obj();
208     if (reply.empty())
209         throw std::runtime_error("expected reply to have result, error and id properties");
210
211     return reply;
212 }
213
214 UniValue RPCCallRoot(const string& strMethod, const UniValue& params, int timeout)
215 {
216     string host, credentials;
217     int port;
218     map<string, string> settings;
219     map<string, vector<string>> settingsmulti;
220
221     if (PBAAS_HOST != "" && PBAAS_PORT != 0)
222     {
223         return RPCCall(strMethod, params, PBAAS_USERPASS, PBAAS_PORT, PBAAS_HOST);
224     }
225     else if (ReadConfigFile(PBAAS_TESTMODE ? "VRSCTEST" : "VRSC", settings, settingsmulti))
226     {
227         PBAAS_USERPASS = settingsmulti.find("-rpcuser")->second[0] + ":" + settingsmulti.find("-rpcpassword")->second[0];
228         PBAAS_PORT = atoi(settingsmulti.find("-rpcport")->second[0]);
229         PBAAS_HOST = settingsmulti.find("-rpchost")->second[0];
230         if (!PBAAS_HOST.size())
231         {
232             PBAAS_HOST = "127.0.0.1";
233         }
234         return RPCCall(strMethod, params, credentials, port, host, timeout);
235     }
236     return UniValue(UniValue::VNULL);
237 }
238
239 UniValue CCrossChainRPCData::ToUniValue() const
240 {
241     UniValue obj(UniValue::VOBJ);
242     obj.push_back(Pair("host", host));
243     obj.push_back(Pair("port", port));
244     obj.push_back(Pair("credentials", credentials));
245     return obj;
246 }
247
248 CNodeData::CNodeData(const UniValue &obj)
249 {
250     networkAddress = uni_get_str(find_value(obj, "networkaddress"));
251     CTxDestination dest = DecodeDestination(uni_get_str(find_value(obj, "nodeidentity")));
252     if (dest.which() != COptCCParams::ADDRTYPE_ID)
253     {
254         nodeIdentity = uint160();
255     }
256     else
257     {
258         nodeIdentity = GetDestinationID(dest);
259     }
260 }
261
262 CNodeData::CNodeData(std::string netAddr, std::string paymentAddr) :
263     networkAddress(netAddr)
264 {
265     nodeIdentity = GetDestinationID(DecodeDestination(paymentAddr));
266 }
267
268 CIdentitySignature::CIdentitySignature(const UniValue &uni)
269 {
270     version = uni_get_int(find_value(uni, "version"));
271     blockHeight = uni_get_int64(find_value(uni, "blockheight"));
272     UniValue sigs = find_value(uni, "signatures");
273     if (sigs.isArray() && sigs.size())
274     {
275         for (int i = 0; i < sigs.size(); i++)
276         {
277             signatures.insert(ParseHex(uni_get_str(sigs[i])));
278         }
279     }
280 }
281
282 uint256 CIdentitySignature::IdentitySignatureHash(const std::vector<uint160> &vdxfCodes, 
283                                                   const std::vector<uint256> &statements, 
284                                                   const uint160 &systemID, 
285                                                   uint32_t blockHeight, 
286                                                   uint160 idID,
287                                                   const std::string &prefixString, 
288                                                   const uint256 &msgHash) const
289 {
290     uint256 retVal;
291     if (version == VERSION_VERUSID)
292     {
293         CHashWriterSHA256 ss(SER_GETHASH, PROTOCOL_VERSION);
294
295         ss << prefixString;
296         ss << systemID;
297         ss << blockHeight;
298         ss << idID;
299         ss << msgHash;
300
301         retVal = ss.GetHash();
302     }
303     else
304     {
305         auto ss = CMMRNode<>::GetHashWriter();
306
307         if (vdxfCodes.size())
308         {
309             ss << vdxfCodes;
310         }
311         if (statements.size())
312         {
313             ss << statements;
314         }
315         ss << systemID;
316         ss << blockHeight;
317         ss << idID;
318         if (prefixString.size())
319         {
320             ss << prefixString;
321         }
322         ss << msgHash;
323         retVal = ss.GetHash();
324     }
325     return retVal;
326 }
327
328 CIdentitySignature::ESignatureVerification CIdentitySignature::CheckSignature(const CIdentity &signingID,
329                                                                               const std::vector<uint160> &vdxfCodes, 
330                                                                               const std::vector<uint256> &statements, 
331                                                                               const uint160 systemID, 
332                                                                               const std::string &prefixString, 
333                                                                               const uint256 &msgHash) const
334 {
335     CPubKey checkKey;
336     std::set<uint160> keys;
337     std::set<uint160> idKeys;
338     for (auto &oneKey : signingID.primaryAddresses)
339     {
340         // we currently only support secp256k1 signatures
341         if (oneKey.which() != COptCCParams::ADDRTYPE_PK && oneKey.which() != COptCCParams::ADDRTYPE_PKH)
342         {
343             return SIGNATURE_INVALID;
344         }
345         idKeys.insert(GetDestinationID(oneKey));
346     }
347     uint256 signatureHash = IdentitySignatureHash(vdxfCodes, statements, systemID, blockHeight, signingID.GetID(), prefixString, msgHash);
348     for (auto &oneSig : signatures)
349     {
350         if (oneSig.size() != ECDSA_RECOVERABLE_SIZE)
351         {
352             return SIGNATURE_INVALID;
353         }
354         if (!checkKey.RecoverCompact(signatureHash, oneSig))
355         {
356             return SIGNATURE_INVALID;
357         }
358         uint160 checkKeyID = checkKey.GetID();
359         if (!idKeys.count(checkKeyID))
360         {
361             return SIGNATURE_INVALID;
362         }
363         keys.insert(checkKey.GetID());
364     }
365     if (keys.size() >= signingID.minSigs)
366     {
367         return SIGNATURE_COMPLETE;
368     }
369     else if (keys.size())
370     {
371         return SIGNATURE_PARTIAL;
372     }
373     else
374     {
375         return SIGNATURE_INVALID;
376     }
377 }
378
379 uint160 DecodeCurrencyName(std::string currencyStr)
380 {
381     uint160 retVal;
382     currencyStr = TrimSpaces(currencyStr);
383     if (!currencyStr.size())
384     {
385         return retVal;
386     }
387     if (currencyStr.back() == '@')
388     {
389         return retVal;
390     }
391     uint160 parent;
392     currencyStr = CleanName(currencyStr, parent, true);
393     if (!parent.IsNull() && CCurrencyDefinition::GetID(currencyStr, parent) == ASSETCHAINS_CHAINID)
394     {
395         return ASSETCHAINS_CHAINID;
396     }
397     CTxDestination currencyDest = DecodeDestination(currencyStr);
398     if (currencyDest.which() == COptCCParams::ADDRTYPE_INVALID)
399     {
400         currencyDest = DecodeDestination(currencyStr + "@");
401     }
402     if (currencyDest.which() != COptCCParams::ADDRTYPE_INVALID)
403     {
404         return GetDestinationID(currencyDest);
405     }
406     return retVal;
407 }
408
409 CCurrencyDefinition::CCurrencyDefinition(const UniValue &obj) :
410     preLaunchDiscount(0),
411     initialFractionalSupply(0),
412     gatewayConverterIssuance(0),
413     minNotariesConfirm(0),
414     idRegistrationFees(IDENTITY_REGISTRATION_FEE),
415     idReferralLevels(DEFAULT_ID_REFERRAL_LEVELS),
416     idImportFees(IDENTITY_IMPORT_FEE),
417     currencyRegistrationFee(CURRENCY_REGISTRATION_FEE),
418     currencyImportFee(CURRENCY_IMPORT_FEE),
419     transactionImportFee(TRANSACTION_TRANSFER_FEE >> 1),
420     transactionExportFee(TRANSACTION_TRANSFER_FEE >> 1)
421 {
422     try
423     {
424         nVersion = uni_get_int64(find_value(obj, "version"), VERSION_CURRENT);
425         options = (uint32_t)uni_get_int64(find_value(obj, "options"));
426         name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
427
428         std::string parentStr = uni_get_str(find_value(obj, "parent"));
429         if (parentStr != "")
430         {
431             parent = DecodeCurrencyName(parentStr);
432             if (parent.IsNull())
433             {
434                 LogPrintf("%s: invalid parent for currency: %s\n", __func__, parentStr.c_str());
435                 nVersion = PBAAS_VERSION_INVALID;
436                 return;
437             }
438         }
439
440         name = CleanName(name, parent);
441
442         std::string systemIDStr = uni_get_str(find_value(obj, "systemid"));
443         if (systemIDStr != "")
444         {
445             systemID = DecodeCurrencyName(systemIDStr);
446             // if we have a system, but it is invalid, the json for this definition cannot be valid
447             if (systemID.IsNull())
448             {
449                 nVersion = PBAAS_VERSION_INVALID;
450                 return;
451             }
452         }
453         else
454         {
455             systemID = parent;
456         }
457
458         gatewayConverterName = uni_get_str(find_value(obj, "gatewayconvertername"));
459         if (!gatewayConverterName.empty())
460         {
461             if (!(IsPBaaSChain() || IsGateway()))
462             {
463                 LogPrintf("%s: a gateway converter currency may only be defined as part of a gateway or PBaaS system definition\n", __func__);
464                 nVersion = PBAAS_VERSION_INVALID;
465                 return;
466             }
467             uint160 gatewayParent = systemID;
468             gatewayID = GetID(gatewayConverterName, gatewayParent);
469         }
470
471         notarizationProtocol = (ENotarizationProtocol)uni_get_int(find_value(obj, "notarizationprotocol"), (int32_t)NOTARIZATION_NOTARY_CONFIRM);
472         if (notarizationProtocol != NOTARIZATION_NOTARY_CONFIRM && !(IsToken() && notarizationProtocol == NOTARIZATION_AUTO))
473         {
474             LogPrintf("%s: notarization protocol for PBaaS chains must be %d (NOTARIZATION_NOTARY_CONFIRM) at this time\n", __func__, (int)NOTARIZATION_NOTARY_CONFIRM);
475             nVersion = PBAAS_VERSION_INVALID;
476             return;
477         }
478         proofProtocol = (EProofProtocol)uni_get_int(find_value(obj, "proofprotocol"), (int32_t)PROOF_PBAASMMR);
479         if (proofProtocol != PROOF_PBAASMMR && proofProtocol != PROOF_CHAINID && proofProtocol != PROOF_ETHNOTARIZATION)
480         {
481             LogPrintf("%s: proofprotocol must be %d, %d, or %d\n", __func__, (int)PROOF_PBAASMMR, (int)PROOF_CHAINID, (int)PROOF_ETHNOTARIZATION);
482             nVersion = PBAAS_VERSION_INVALID;
483             return;
484         }
485
486         nativeCurrencyID = CTransferDestination(find_value(obj, "nativecurrencyid"));
487
488         std::string launchIDStr = uni_get_str(find_value(obj, "launchsystemid"));
489         if (launchIDStr != "")
490         {
491             launchSystemID = DecodeCurrencyName(launchIDStr);
492             // if we have a system, but it is invalid, the json for this definition cannot be valid
493             if (launchSystemID.IsNull())
494             {
495                 nVersion = PBAAS_VERSION_INVALID;
496                 return;
497             }
498         }
499         else
500         {
501             launchSystemID = parent;
502         }
503
504         startBlock = uni_get_int(find_value(obj, "startblock"));
505         endBlock = uni_get_int(find_value(obj, "endblock"));
506
507         int32_t totalReserveWeight = IsFractional() ? SATOSHIDEN : 0;
508         UniValue currencyArr = find_value(obj, "currencies");
509         UniValue weightArr = find_value(obj, "weights");
510         UniValue conversionArr = find_value(obj, "conversions");
511         UniValue minPreconvertArr = find_value(obj, "minpreconversion");
512         UniValue maxPreconvertArr = find_value(obj, "maxpreconversion");
513         UniValue initialContributionArr = find_value(obj, "initialcontributions");
514
515         if (currencyArr.isArray() && currencyArr.size())
516         {
517             contributions = preconverted = std::vector<int64_t>(currencyArr.size());
518
519             if (IsFractional())
520             {
521                 preLaunchDiscount = AmountFromValueNoErr(find_value(obj, "prelaunchdiscount"));
522                 initialFractionalSupply = AmountFromValueNoErr(find_value(obj, "initialsupply"));
523                 gatewayConverterIssuance = AmountFromValueNoErr(find_value(obj, "gatewayconverterissuance"));
524
525                 if (!initialFractionalSupply)
526                 {
527                     LogPrintf("%s: cannot specify zero initial supply for fractional currency\n", __func__);
528                     printf("%s: cannot specify zero initial supply for fractional currency\n", __func__);
529                     nVersion = PBAAS_VERSION_INVALID;
530                 }
531
532                 UniValue preLaunchCarveoutsUni = find_value(obj, "prelaunchcarveouts");
533                 int32_t preLaunchCarveOutTotal = 0;
534                 if (nVersion != PBAAS_VERSION_INVALID && preLaunchCarveoutsUni.isArray())
535                 {
536                     for (int i = 0; i < preLaunchCarveoutsUni.size(); i++)
537                     {
538                         std::vector<std::string> preLaunchCarveOutKey = preLaunchCarveoutsUni[i].getKeys();
539                         std::vector<UniValue> preLaunchCarveOutValue = preLaunchCarveoutsUni[i].getValues();
540                         if (preLaunchCarveOutKey.size() != 1 || preLaunchCarveOutValue.size() != 1)
541                         {
542                             LogPrintf("%s: each prelaunchcarveouts entry must contain one destination identity and one amount\n", __func__);
543                             printf("%s: each prelaunchcarveouts entry must contain one destination identity and one amount\n", __func__);
544                             nVersion = PBAAS_VERSION_INVALID;
545                             break;
546                         }
547
548                         CTxDestination carveOutDest = DecodeDestination(preLaunchCarveOutKey[0]);
549
550                         if (carveOutDest.which() != COptCCParams::ADDRTYPE_ID && !(carveOutDest.which() == COptCCParams::ADDRTYPE_INVALID && preLaunchCarveoutsUni.size() == 1))
551                         {
552                             LogPrintf("%s: prelaunchcarveouts destination must be an identity\n", __func__);
553                             nVersion = PBAAS_VERSION_INVALID;
554                             break;
555                         }
556
557                         CAmount carveOutAmount = AmountFromValueNoErr(preLaunchCarveOutValue[0]);
558                         if (carveOutAmount <= 0)
559                         {
560                             LogPrintf("%s: prelaunchcarveouts values must be greater than zero\n", __func__);
561                             nVersion = PBAAS_VERSION_INVALID;
562                             break;
563                         }
564                         preLaunchCarveOutTotal += carveOutAmount;
565                         if (preLaunchDiscount < 0 || 
566                             preLaunchDiscount >= SATOSHIDEN ||
567                             CCurrencyDefinition::CalculateRatioOfValue((totalReserveWeight - preLaunchCarveOutTotal), SATOSHIDEN - preLaunchDiscount) >= SATOSHIDEN)
568                         {
569                             LogPrintf("%s: prelaunchcarveouts values and discounts must total less than 1\n", __func__);
570                             nVersion = PBAAS_VERSION_INVALID;
571                             break;
572                         }
573                         preLaunchCarveOuts.push_back(make_pair(CIdentityID(GetDestinationID(carveOutDest)), preLaunchCarveOutTotal));
574                     }
575                 }
576
577                 // if weights are defined, use them as relative ratios of each member currency
578                 if (weightArr.isArray() && weightArr.size())
579                 {
580                     if (weightArr.size() != currencyArr.size())
581                     {
582                         LogPrintf("%s: reserve currency weights must be specified for all currencies\n", __func__);
583                         nVersion = PBAAS_VERSION_INVALID;
584                     }
585                     else
586                     {
587                         CAmount total = 0;
588                         for (int i = 0; i < currencyArr.size(); i++)
589                         {
590                             int32_t weight = (int32_t)AmountFromValueNoErr(weightArr[i]);
591                             if (weight <= 0)
592                             {
593                                 nVersion = PBAAS_VERSION_INVALID;
594                                 total = 0;
595                                 break;
596                             }
597                             total += weight;
598                             weights.push_back(weight);
599                         }
600                         if (nVersion != PBAAS_VERSION_INVALID)
601                         {
602                             // calculate each weight as a relative part of the total
603                             // reserve weight
604                             int64_t totalRelativeWeight = 0;
605                             for (auto &onew : weights)
606                             {
607                                 totalRelativeWeight += onew;
608                             }
609
610                             int weightIdx;
611                             arith_uint256 bigReserveWeight(totalReserveWeight);
612                             int32_t reserveLeft = totalReserveWeight;
613                             for (weightIdx = 0; weightIdx < weights.size(); weightIdx++)
614                             {
615                                 CAmount amount = (bigReserveWeight * arith_uint256(weights[weightIdx]) / arith_uint256(totalRelativeWeight)).GetLow64();
616                                 if (reserveLeft <= amount || (weightIdx + 1) == weights.size())
617                                 {
618                                     amount = reserveLeft;
619                                 }
620                                 reserveLeft -= amount;
621                                 weights[weightIdx] = amount;
622                             }
623                         }
624                     }
625                 }
626                 else if (totalReserveWeight)
627                 {
628                     uint32_t oneWeight = totalReserveWeight / currencyArr.size();
629                     uint32_t mod = totalReserveWeight % currencyArr.size();
630                     for (int i = 0; i < currencyArr.size(); i++)
631                     {
632                         // distribute remainder of weight among first come currencies
633                         int32_t weight = oneWeight;
634                         if (mod > 0)
635                         {
636                             weight++;
637                             mod--;
638                         }
639                         weights.push_back(weight);
640                     }
641                 }
642             }
643
644             // if we have weights, we can be a fractional currency
645             if (weights.size())
646             {
647                 // if we are fractional, explicit conversion values are not valid
648                 // and are based on non-zero, initial contributions relative to supply
649                 if ((conversionArr.isArray() && conversionArr.size()) ||
650                     !initialContributionArr.isArray() || 
651                     initialContributionArr.size() != currencyArr.size() ||
652                     weights.size() != currencyArr.size() ||
653                     !IsFractional())
654                 {
655                     LogPrintf("%s: reserve currencies must have weights, initial contributions in every currency, and no explicit conversion rates\n", __func__);
656                     nVersion = PBAAS_VERSION_INVALID;
657                 }
658             }
659             else
660             {
661                 // if we are not a reserve currency, we either have a conversion vector, or we are not convertible at all
662                 if (IsFractional())
663                 {
664                     LogPrintf("%s: reserve currencies must define currency weight\n", __func__);
665                     nVersion = PBAAS_VERSION_INVALID;
666                 }
667                 else if (conversionArr.isArray() && conversionArr.size() && conversionArr.size() != currencyArr.size())
668                 {
669                     LogPrintf("%s: non-reserve currencies must define all conversion rates for supported currencies if they define any\n", __func__);
670                     nVersion = PBAAS_VERSION_INVALID;
671                 }
672                 else if (initialContributionArr.isArray() && initialContributionArr.size() != currencyArr.size())
673                 {
674                     LogPrintf("%s: initial contributions for currencies must all be specified if any are specified\n", __func__);
675                     nVersion = PBAAS_VERSION_INVALID;
676                 }
677             }
678
679             if (nVersion != PBAAS_VERSION_INVALID && IsFractional())
680             {
681                 if (minPreconvertArr.isArray() && minPreconvertArr.size() && minPreconvertArr.size() != currencyArr.size())
682                 {
683                     LogPrintf("%s: currencies with minimum conversion required must define all minimums if they define any\n", __func__);
684                     nVersion = PBAAS_VERSION_INVALID;
685                 }
686                 if (maxPreconvertArr.isArray() && maxPreconvertArr.size() && maxPreconvertArr.size() != currencyArr.size())
687                 {
688                     LogPrintf("%s: currencies that include maximum conversions on pre-launch must specify all maximums\n", __func__);
689                     nVersion = PBAAS_VERSION_INVALID;
690                 }
691                 if (initialContributionArr.isArray() && initialContributionArr.size() && initialContributionArr.size() != currencyArr.size())
692                 {
693                     LogPrintf("%s: currencies that include initial contributions in one currency on pre-launch must specify all currency amounts\n", __func__);
694                     nVersion = PBAAS_VERSION_INVALID;
695                 }
696             }
697
698             bool isInitialContributions = initialContributionArr.isArray() && initialContributionArr.size();
699             bool isPreconvertMin = minPreconvertArr.isArray() && minPreconvertArr.size();
700             bool isPreconvertMax = maxPreconvertArr.isArray() && maxPreconvertArr.size();
701             bool explicitConversions = conversionArr.isArray() && conversionArr.size();
702
703             if (IsFractional() && explicitConversions)
704             {
705                 LogPrintf("%s: cannot specify explicit conversion values for reserve currencies\n", __func__);
706                 nVersion = PBAAS_VERSION_INVALID;
707             }
708
709             for (int i = 0; nVersion != PBAAS_VERSION_INVALID && i < currencyArr.size(); i++)
710             {
711                 uint160 currencyID = DecodeCurrencyName(uni_get_str(currencyArr[i]));
712                 // if we have a destination, but it is invalid, the json for this definition cannot be valid
713                 if (currencyID.IsNull())
714                 {
715                     nVersion = PBAAS_VERSION_INVALID;
716                     break;
717                 }
718                 else
719                 {
720                     currencies.push_back(currencyID);
721                 }
722
723                 if (isInitialContributions && i < initialContributionArr.size())
724                 {
725                     int64_t contrib = AmountFromValueNoErr(initialContributionArr[i]);
726                     if (IsFractional() && contrib < MIN_RESERVE_CONTRIBUTION)
727                     {
728                         LogPrintf("%s: all fractional reserve currencies must start with %s minimum initial contribution in each currency\n", __func__, ValueFromAmount(MIN_RESERVE_CONTRIBUTION).write().c_str());
729                         nVersion = PBAAS_VERSION_INVALID;
730                         break;
731                     }
732                     contributions[i] = contrib;
733                     preconverted[i] = contrib;
734                 }
735
736                 int64_t minPre = 0;
737                 if (isPreconvertMin)
738                 {
739                     minPre = AmountFromValueNoErr(minPreconvertArr[i]);
740                     if (minPre < 0)
741                     {
742                         LogPrintf("%s: minimum preconversions for any currency may not be less than 0\n", __func__);
743                         nVersion = PBAAS_VERSION_INVALID;
744                         break;
745                     }
746                     minPreconvert.push_back(minPre);
747                 }
748                 if (isPreconvertMax)
749                 {
750                     int64_t maxPre = AmountFromValueNoErr(maxPreconvertArr[i]);
751                     if (maxPre < 0 || maxPre < minPre)
752                     {
753                         LogPrintf("%s: maximum preconversions for any currency may not be less than 0 or minimum\n", __func__);
754                         nVersion = PBAAS_VERSION_INVALID;
755                         break;
756                     }
757                     maxPreconvert.push_back(maxPre);
758                 }
759                 if (explicitConversions)
760                 {
761                     int64_t conversion = AmountFromValueNoErr(conversionArr[i]);
762                     if (conversion < 0)
763                     {
764                         LogPrintf("%s: conversions for any currency must be greater than 0\n", __func__);
765                         nVersion = PBAAS_VERSION_INVALID;
766                         break;
767                     }
768                     conversions.push_back(conversion);
769                 }
770                 else
771                 {
772                     conversions.push_back(0);
773                 }
774             }
775         }
776
777         UniValue preallocationArr = find_value(obj, "preallocations");
778         if (preallocationArr.isArray())
779         {
780             for (int i = 0; i < preallocationArr.size(); i++)
781             {
782                 std::vector<std::string> preallocationKey = preallocationArr[i].getKeys();
783                 std::vector<UniValue> preallocationValue = preallocationArr[i].getValues();
784                 if (preallocationKey.size() != 1 || preallocationValue.size() != 1)
785                 {
786                     LogPrintf("%s: each preallocation entry must contain one destination identity and one amount\n", __func__);
787                     printf("%s: each preallocation entry must contain one destination identity and one amount\n", __func__);
788                     nVersion = PBAAS_VERSION_INVALID;
789                     break;
790                 }
791
792                 CTxDestination preallocDest = DecodeDestination(preallocationKey[0]);
793
794                 if (preallocDest.which() != COptCCParams::ADDRTYPE_ID && preallocDest.which() != COptCCParams::ADDRTYPE_INVALID)
795                 {
796                     LogPrintf("%s: preallocation destination must be an identity\n", __func__);
797                     nVersion = PBAAS_VERSION_INVALID;
798                     break;
799                 }
800
801                 CAmount preAllocAmount = AmountFromValueNoErr(preallocationValue[0]);
802                 if (preAllocAmount <= 0)
803                 {
804                     LogPrintf("%s: preallocation values must be greater than zero\n", __func__);
805                     nVersion = PBAAS_VERSION_INVALID;
806                     break;
807                 }
808                 preAllocation.push_back(make_pair(CIdentityID(GetDestinationID(preallocDest)), preAllocAmount));
809             }
810         }
811
812         UniValue notaryArr = find_value(obj, "notaries");
813         minNotariesConfirm = 0;
814         if (notaryArr.isArray())
815         {
816             for (int i = 0; i < notaryArr.size(); i++)
817             {
818                 CIdentityID notaryID;
819                 CTxDestination notaryDest = DecodeDestination(uni_get_str(notaryArr[i]));
820                 notaryID = GetDestinationID(notaryDest);
821                 // if we have a destination, but it is invalid, the json for this definition cannot be valid
822                 if (notaryID.IsNull())
823                 {
824                     nVersion = PBAAS_VERSION_INVALID;
825                 }
826                 else
827                 {
828                     notaries.push_back(notaryID);
829                 }
830             }
831             minNotariesConfirm = uni_get_int(find_value(obj, "minnotariesconfirm"));
832         }
833
834         idRegistrationFees = uni_get_int64(find_value(obj, "idregistrationfees"), idRegistrationFees);
835         idReferralLevels = uni_get_int(find_value(obj, "idreferrallevels"), idReferralLevels);
836         idImportFees = uni_get_int64(find_value(obj, "idimportfees"), idImportFees);
837
838         auto vEras = uni_getValues(find_value(obj, "eras"));
839         if (vEras.size() > ASSETCHAINS_MAX_ERAS)
840         {
841             vEras.resize(ASSETCHAINS_MAX_ERAS);
842         }
843
844         if (vEras.size())
845         {
846             currencyRegistrationFee = uni_get_int64(find_value(obj, "currencyregistrationfee"), currencyRegistrationFee);
847             currencyImportFee = uni_get_int64(find_value(obj, "currencyimportfee"), currencyImportFee);
848             transactionImportFee = uni_get_int64(find_value(obj, "transactionimportfee"), transactionImportFee);
849             transactionExportFee = uni_get_int64(find_value(obj, "transactionexportfee"), transactionExportFee);
850
851             if (!gatewayID.IsNull())
852             {
853                 gatewayConverterIssuance = uni_get_int64(find_value(obj, "gatewayconverterissuance"));
854             }
855
856             for (auto era : vEras)
857             {
858                 rewards.push_back(uni_get_int64(find_value(era, "reward")));
859                 rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
860                 halving.push_back(uni_get_int64(find_value(era, "halving")));
861                 eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
862             }
863
864             if (!rewards.size())
865             {
866                 LogPrintf("%s: PBaaS chain does not have valid rewards eras");
867                 nVersion = PBAAS_VERSION_INVALID;
868             }
869         }
870     }
871     catch (exception e)
872     {
873         LogPrintf("%s: exception reading currency definition JSON\n", __func__, e.what());
874         nVersion = PBAAS_VERSION_INVALID;
875     }
876 }
877
878 int64_t CCurrencyDefinition::CalculateRatioOfValue(int64_t value, int64_t ratio)
879 {
880     arith_uint256 bigAmount(value);
881     static const arith_uint256 bigSatoshi(SATOSHIDEN);
882
883     int64_t retVal = ((bigAmount * arith_uint256(ratio)) / bigSatoshi).GetLow64();
884     return retVal;
885 }
886
887 // this will only return an accurate result after total preconversion has been updated and before any emission
888 int64_t CCurrencyDefinition::GetTotalPreallocation() const
889 {
890     CAmount totalPreallocatedNative = 0;
891     for (auto &onePreallocation : preAllocation)
892     {
893         totalPreallocatedNative += onePreallocation.second;
894     }
895     return totalPreallocatedNative;
896 }
897
898 // this will only return an accurate result after total preconversion has been updated and before any emission
899 int32_t CCurrencyDefinition::GetTotalCarveOut() const
900 {
901     int32_t totalCarveOut = 0;
902     for (auto &oneCarveOut : preLaunchCarveOuts)
903     {
904         totalCarveOut += oneCarveOut.second;
905         if (oneCarveOut.second < 0 || oneCarveOut.second > SATOSHIDEN || totalCarveOut > SATOSHIDEN)
906         {
907             LogPrintf("%s: invalid carve out amount specified %d\n", __func__, oneCarveOut.second);
908             return 0;
909         }
910     }
911     return totalCarveOut;
912 }
913
This page took 0.076384 seconds and 4 git commands to generate.