diff --git a/src/cc/CCcustom.cpp b/src/cc/CCcustom.cpp index 19772a8f7e2..ce8d607fc01 100644 --- a/src/cc/CCcustom.cpp +++ b/src/cc/CCcustom.cpp @@ -330,9 +330,11 @@ struct CCcontract_info *CCinit(struct CCcontract_info *cp, uint8_t evalcode) cp->ismyvin = IsServiceRewardInput; break; - case EVAL_RESERVEIMPORT: - case EVAL_RESERVEEXPORT: - case EVAL_RESERVEOUTPUT: + case EVAL_INSTANTSPEND: + case EVAL_CROSSCHAIN_INPUT: + case EVAL_CROSSCHAIN_OUTPUT: + case EVAL_CROSSCHAIN_IMPORT: + case EVAL_CROSSCHAIN_EXPORT: assert(false); break; diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index f884e3b12e1..96cb12c25a3 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -73,15 +73,23 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) std::vector vparams(cond->code+1, cond->code+cond->codeLength); switch ( ecode ) { - case EVAL_STAKEGUARD: case EVAL_PBAASDEFINITION: case EVAL_SERVICEREWARD: case EVAL_EARNEDNOTARIZATION: case EVAL_ACCEPTEDNOTARIZATION: case EVAL_FINALIZENOTARIZATION: - case EVAL_RESERVEOUTPUT: - case EVAL_RESERVEEXPORT: - case EVAL_RESERVEIMPORT: + case EVAL_CROSSCHAIN_OUTPUT: + case EVAL_CROSSCHAIN_EXPORT: + case EVAL_CROSSCHAIN_IMPORT: + case EVAL_INSTANTSPEND: + case EVAL_CROSSCHAIN_INPUT: + if (!chainActive.LastTip() || CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) < CActivationHeight::SOLUTION_VERUSV3) + { + // if chain is not able to process this yet, don't drop through to do so + break; + } + + case EVAL_STAKEGUARD: return(ProcessCC(cp,this, vparams, txTo, nIn)); break; diff --git a/src/cc/eval.h b/src/cc/eval.h index 4a3fb0127ee..7f34cda65d2 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -44,12 +44,16 @@ EVAL(EVAL_EARNEDNOTARIZATION, 0x4) \ EVAL(EVAL_ACCEPTEDNOTARIZATION, 0x5) \ EVAL(EVAL_FINALIZENOTARIZATION, 0x6) \ - EVAL(EVAL_RESERVEOUTPUT, 0x7) \ - EVAL(EVAL_RESERVEEXPORT, 0x8) \ - EVAL(EVAL_RESERVEIMPORT, 0x9) \ + EVAL(EVAL_INSTANTSPEND, 0x7) \ + EVAL(EVAL_CROSSCHAIN_INPUT, 0x8) \ + EVAL(EVAL_CROSSCHAIN_OUTPUT, 0x9) \ + EVAL(EVAL_CROSSCHAIN_EXPORT, 0xa) \ + EVAL(EVAL_CROSSCHAIN_IMPORT, 0xb) \ + EVAL(EVAL_RESERVE_OUTPUT, 0xc) \ + EVAL(EVAL_RESERVE_EXCHANGE, 0xd) \ EVAL(EVAL_IMPORTPAYOUT, 0xe1) \ - EVAL(EVAL_IMPORTCOIN, 0xe2) \ - EVAL(EVAL_ASSETS, 0xe3) \ + EVAL(EVAL_IMPORTCOIN, 0xe2) \ + EVAL(EVAL_ASSETS, 0xe3) \ EVAL(EVAL_FAUCET, 0xe4) \ EVAL(EVAL_REWARDS, 0xe5) \ EVAL(EVAL_DICE, 0xe6) \ diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 67b8f5d6322..fb5bc7601f8 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1640,7 +1640,7 @@ bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height) { txnouttype tp; std::vector> vvch = std::vector>(); - // solve all outputs to check that destinations all go only to the pk + // solve all outputs to check that non-instantspend destinations all go only to the pk // specified in the stake params if ((!supportInstantSpend || !vout.scriptPubKey.IsInstantSpend()) && (!Solver(vout.scriptPubKey, tp, vvch) || diff --git a/src/main.cpp b/src/main.cpp index b46f06fd311..06b08922c64 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1593,6 +1593,7 @@ bool AcceptToMemoryPoolInt(CTxMemPool& pool, CValidationState &state, const CTra { return error("AcceptToMemoryPool: non-coinbase with null input tx"); } + /* CTransaction inputTx; uint256 blkHash; if (myGetTransaction(tx.vin[j].prevout.hash, inputTx, blkHash)) @@ -1609,13 +1610,13 @@ bool AcceptToMemoryPoolInt(CTxMemPool& pool, CValidationState &state, const CTra printf("transaction input from %s on input %d is not a notarization\n", tx.vin[j].prevout.hash.GetHex().c_str(), tx.vin[j].prevout.n); } } + */ } } - printf("%s%s at height %d has no outputs\n", tx.IsCoinBase() ? "coinbase transaction" : "transaction #", tx.GetHash().GetHex().c_str(), simHeight); - LogPrintf("%s%s at height %d has no outputs\n", tx.IsCoinBase() ? "coinbase transaction" : "transaction #", tx.GetHash().GetHex().c_str(), simHeight); + //printf("%s%s at height %d has no outputs\n", tx.IsCoinBase() ? "coinbase transaction" : "transaction #", tx.GetHash().GetHex().c_str(), simHeight); + //LogPrintf("%s%s at height %d has no outputs\n", tx.IsCoinBase() ? "coinbase transaction" : "transaction #", tx.GetHash().GetHex().c_str(), simHeight); } - auto verifier = libzcash::ProofVerifier::Strict(); if ( komodo_validate_interest(tx,chainActive.LastTip()->GetHeight()+1,chainActive.LastTip()->GetMedianTimePast() + 777,0) < 0 ) { diff --git a/src/pbaas/notarization.h b/src/pbaas/notarization.h index 0eeb0afe380..d2e8ee32a6d 100644 --- a/src/pbaas/notarization.h +++ b/src/pbaas/notarization.h @@ -203,6 +203,13 @@ class CNotarizationFinalization { return ::AsVector(*this); } + + UniValue ToUniValue() const + { + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("confirmedinput", confirmedInput)); + return ret; + } }; class CChainNotarizationData diff --git a/src/pbaas/pbaas.cpp b/src/pbaas/pbaas.cpp index a5af056aa61..418bd706255 100644 --- a/src/pbaas/pbaas.cpp +++ b/src/pbaas/pbaas.cpp @@ -404,6 +404,16 @@ CServiceReward::CServiceReward(const CTransaction &tx, bool validate) } +CCrossChainInput::CCrossChainInput(const CTransaction &tx) +{ + // TODO - finish +} + +CCrossChainInput::CCrossChainInput(const UniValue &obj) +{ + // TODO - finish +} + uint160 CPBaaSChainDefinition::GetChainID(std::string name) { const char *chainName = name.c_str(); @@ -764,10 +774,11 @@ vector> CConnectedChains::SubmitQualifiedBlocks() inHeader.insert(pbh.chainID); } + uint160 chainID; // now look through all targets that are equal to or above the hash of this header for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++) { - uint160 chainID = chainIt->second->GetChainID(); + chainID = chainIt->second->GetChainID(); if (inHeader.count(chainID)) { // first, check that the winning header matches the block that is there @@ -782,9 +793,6 @@ vector> CConnectedChains::SubmitQualifiedBlocks() *(CBlockHeader *)&chainData.block = headerIt->second; - // once it is going to be submitted, remove block from this chain until a new one is added again - RemoveMergedBlock(chainID); - submissionFound = true; } //else // not an error condition. code is here for debugging @@ -799,7 +807,13 @@ vector> CConnectedChains::SubmitQualifiedBlocks() } // if this header matched no block, discard and move to the next, otherwise, we'll drop through - if (!submissionFound) + if (submissionFound) + { + // once it is going to be submitted, remove block from this chain until a new one is added again + RemoveMergedBlock(chainID); + break; + } + else { qualifiedHeaders.erase(headerIt); } diff --git a/src/pbaas/pbaas.h b/src/pbaas/pbaas.h index c7834922000..ce44c94da4a 100644 --- a/src/pbaas/pbaas.h +++ b/src/pbaas/pbaas.h @@ -54,7 +54,6 @@ static const uint32_t PBAAS_VERSION = 1; static const uint32_t PBAAS_VERSION_INVALID = 0; static const uint32_t PBAAS_NODESPERNOTARIZATION = 2; // number of nodes to reference in each notarization static const int64_t PBAAS_MINNOTARIZATIONOUTPUT = 10000; // enough for one fee worth to finalization and notarization thread -static const int64_t PBAAS_MAXNOTARIZATIONINPUTS = 50; // max inputs on a notarization tx static const int32_t PBAAS_MINSTARTBLOCKDELTA = 100; // minimum number of blocks to wait for starting a chain after definition static const int32_t PBAAS_MAXPRIORBLOCKS = 16; // maximum prior block commitments to include in prior blocks chain object @@ -403,12 +402,14 @@ class CPBaaSChainDefinition static const int64_t MIN_PER_BLOCK_NOTARIZATION = 1000000; // 0.01 VRSC per block notarization minimum static const int64_t MIN_BILLING_PERIOD = 480; // 8 hour minimum billing period for notarization, typically expect days/weeks/months static const int64_t DEFAULT_OUTPUT_VALUE = 100000; // 0.001 VRSC default output value + static const int32_t OPTION_RESERVE = 1; // allows reserve conversion using base calculations when set + static const int32_t OPTION_CONVERT = 2; // allows conversion to the currency from Verus if set uint32_t nVersion; // version of this chain definition data structure to allow for extensions (not daemon version) std::string name; // chain name, maximum 64 characters CKeyID address; // non-purchased/converted premine and fee recipient address int64_t premine; // initial supply that is distributed to the premine output address, but not purchased - int64_t conversion; // factor / 100000000 for conversion of VRSC to coin on launch + int64_t conversion; // factor / 100000000 for conversion of VRSC to/from coin, prior to chain start only for reserve int64_t launchFee; // ratio of satoshis to send from contribution to convertible to fee address int32_t startBlock; // parent chain block # that must kickoff the notarization of block 0, cannot be before this block int32_t endBlock; // block after which this is considered end-of-lifed @@ -633,30 +634,36 @@ class CCrossChainProof } }; -// This is used on a PBaaS chain to manage reserves of VRSC. type of reserve is assumed to be $VRSC -class CReserveOutput +// this is used on either the VRSC chain or PBaaS chain to send transactions to an alternate chain, which will +// be realized on the other chain using the specified output script. +// To actually realize the transfer of funds to or from a PBaaS chain, inputs must be aggregated by a miner as inputs to +// a CCrossChainExport transaction, which may then be imported to the PBaaS chain with one transaction proof proving all inputs +// being converted to outputs in bulk. This avoids creating the overhead of many redundant transaction proofs +// which can move a large number of smaller transactions across chains. +class CCrossChainInput { public: - CAmount nValue; // amount of reserve coin in this output - CScript scrOut; // output script, spend is validated as with a normal bitcoin spend + CAmount finalValue; // difference between actual output of this tx and final is fee paid + CScript scriptPubKey; // output script, spend is validated as with a normal bitcoin spend - CReserveOutput(const std::vector &asVector) + CCrossChainInput() : finalValue(-1) {} + + CCrossChainInput(const std::vector &asVector) { FromVector(asVector, *this); } - CReserveOutput(const CAmount value, const CScript &rScrOut) - { - nValue = value; - scrOut = rScrOut; - } + CCrossChainInput(const CScript &rScrOut, const CAmount finalout) : scriptPubKey(rScrOut), finalValue(finalout) { } + + CCrossChainInput(const CTransaction &tx); + CCrossChainInput(const UniValue &obj); ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(VARINT(nValue)); - READWRITE(*(CScriptBase*)(&scrOut)); + READWRITE(finalValue); + READWRITE(*(CScriptBase*)(&scriptPubKey)); } std::vector AsVector() @@ -666,26 +673,68 @@ class CReserveOutput bool IsValid() { - return scrOut.size() != 0; + return scriptPubKey.size() != 0 && finalValue >= 0; + } + + UniValue ToUniValue() + { + UniValue ret(UniValue::VOBJ); + ret.push_back(Pair("finalvalue", finalValue)); + ret.push_back(Pair("scriptpubkey", scriptPubKey.ToString())); + return ret; } }; -// import $VRSC from another chain transaction -class CReserveImport +// This is used on a PBaaS chain to transact with reserves of VRSC as a token that can freely convert between +// the native coin and the token type. type of reserve is assumed to be $VRSC +class CCrossChainOutput { public: - CAmount nValue; // amount of proxy coin in this output - CCrossChainProof transferProof; + uint160 chainID; // from what chain + CAmount nValue; // amount of token in this output + CScript scriptPubKey; // output script, spend is validated as with a normal spend - CReserveImport(const std::vector &asVector) + CCrossChainOutput(uint160 cID, const CScript &rScrOut, const CAmount value) : chainID(cID), nValue(value), scriptPubKey(rScrOut) { } + CCrossChainOutput(const std::vector &asVector) { FromVector(asVector, *this); } - CReserveImport(const CAmount value, CCrossChainProof &rTransferProof) + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(chainID); + READWRITE(VARINT(nValue)); + READWRITE(*(CScriptBase*)(&scriptPubKey)); + } + + std::vector AsVector() + { + return ::AsVector(*this); + } + + bool IsValid() + { + return scriptPubKey.size() != 0 && !chainID.IsNull() || nValue >= 0; + } +}; + +// import transactions from another chain +// an import transaction on a fractional reserve chain will have an instant spend input of EVAL_CHAIN_IMPORT from the coinbase, +// which provides for import of a reserve currency or cross-chain token, or auto-conversion to the native currency. +class CCrossChainImport +{ +public: + uint160 chainID; // usually the reserve currency, but here for generality + CAmount nValue; // amount of proxy coin for final output (difference from actual output divided into 2 fees) + + CCrossChainImport() : nValue(0) { } + CCrossChainImport(const CAmount value) : nValue(value) { } + + CCrossChainImport(const std::vector &asVector) { - nValue = value; - transferProof = rTransferProof; + FromVector(asVector, *this); } ADD_SERIALIZE_METHODS; @@ -693,7 +742,6 @@ class CReserveImport template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(VARINT(nValue)); - READWRITE(transferProof); } std::vector AsVector() @@ -703,34 +751,44 @@ class CReserveImport bool IsValid() { - return nValue != 0 && transferProof.IsValid(); + return nValue >= 0 && !chainID.IsNull(); } }; -// send $VRSC or VRSCTOKEN to another chain -class CReserveExport +// send some amount of $VRSC as VRSCTOKEN to another chain for distribution to original senders, which are +// determined by the inputs to this transaction and stored in the opret of this transaction, so they can be +// validated on the destination chain as one transaction, yet used as the output scripts of the transaction +// itself +// The export type determines how the coins are realized on the destination chain, but the destination +// script will be used in any case. +class CCrossChainExport { public: + enum EXPORT_TYPE { + EXPORT_INVALID = 0, + EXPORT_CONVERSION = 1, // realized on the destination chain as the destination chain currency at market conversion rate + EXPORT_SEND = 2 // realized on destination chain as a representative reserve token, unconverted + }; + uint8_t exportType; CAmount nValue; - uint256 chainDest; + uint160 chainID; - CReserveExport(const std::vector &asVector) + CCrossChainExport() : exportType(EXPORT_INVALID), nValue(0) {} + + CCrossChainExport(const std::vector &asVector) { FromVector(asVector, *this); } - CReserveExport(const CAmount value, const uint256 &rChainDest) - { - nValue = value; - chainDest = rChainDest; - } + CCrossChainExport(EXPORT_TYPE exporttype, const CAmount value, const uint160 &rChainDest) : exportType(exporttype), nValue(value), chainID(rChainDest) {} ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { + READWRITE(exportType); READWRITE(VARINT(nValue)); - READWRITE(chainDest); + READWRITE(chainID); } std::vector AsVector() @@ -740,7 +798,7 @@ class CReserveExport bool IsValid() { - return nValue != 0 && !chainDest.IsNull(); + return exportType != EXPORT_INVALID && nValue >= 0 && !chainID.IsNull(); } }; @@ -748,29 +806,28 @@ class CReserveExport class CReserveExchange { public: - CScript scrOut; // output script for resulting coinbase output + uint32_t flags; // control of direction and constraints + CScript scriptPubKey; // output script for resulting coinbase output CAmount nLimit; // lowest or highest price to sell or buy coin output, may fail if including this tx in block makes price out of range uint32_t nValidBefore; // if not filled in this block, mine tx, but refund input - uint256 chainID; // currently supports convert from or to reserve according to conversion rules, this is ouput type + uint160 chainID; // currently supports convert from or to reserve according to conversion rules, this is ouput type CReserveExchange(const std::vector &asVector) { FromVector(asVector, *this); } - CReserveExchange(const CScript &rScrOut, const CAmount Limit, uint32_t ValidBefore, uint256 ChainID) - { - scrOut = rScrOut; - nLimit = Limit; - nValidBefore = ValidBefore; - chainID = ChainID; - } + CReserveExchange() : flags(0), nLimit(0), nValidBefore(0) { } + + CReserveExchange(uint32_t Flags, const CScript &rScrOut, const CAmount Limit, uint32_t ValidBefore, uint160 ChainID) : + flags(Flags), scriptPubKey(rScrOut), nLimit(Limit), nValidBefore(ValidBefore), chainID(ChainID) { } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { - READWRITE(*(CScriptBase*)(&scrOut)); + READWRITE(flags); + READWRITE(*(CScriptBase*)(&scriptPubKey)); READWRITE(VARINT(nLimit)); READWRITE(nValidBefore); READWRITE(chainID); @@ -784,7 +841,7 @@ class CReserveExchange bool IsValid() { // this needs an actual check - return nValidBefore != 0 && scrOut.size() != 0 && !chainID.IsNull(); + return nValidBefore != 0 && scriptPubKey.size() != 0 && !chainID.IsNull(); } }; diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 23b8d7071c0..ad76b307d0d 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -698,7 +698,7 @@ class CTransaction bool IsCoinImport() const { // return (vin.size() == 1 && vin[0].prevout.n == 10e8); - false; // we don't support "importing" coins this way + return false; // we don't support "importing" coins this way } friend bool operator==(const CTransaction& a, const CTransaction& b) diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 280e7a17365..c7b556e1d35 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1459,7 +1459,6 @@ UniValue getchaintips(const UniValue& params, bool fHelp) /* Construct the output array. */ UniValue res(UniValue::VARR); const CBlockIndex *forked; - BOOST_FOREACH(const CBlockIndex* block, setTips) BOOST_FOREACH(const CBlockIndex* block, setTips) { UniValue obj(UniValue::VOBJ); diff --git a/src/rpc/pbaasrpc.cpp b/src/rpc/pbaasrpc.cpp index 6b12c6a8aad..18024b91266 100644 --- a/src/rpc/pbaasrpc.cpp +++ b/src/rpc/pbaasrpc.cpp @@ -143,6 +143,15 @@ bool GetChainDefinition(uint160 chainID, CPBaaSChainDefinition &chainDef) return true; } + if (!IsVerusActive()) + { + if (ConnectedChains.NotaryChain().IsValid() && (chainID == ConnectedChains.NotaryChain().chainDefinition.GetChainID())) + { + chainDef = ConnectedChains.NotaryChain().chainDefinition; + return true; + } + } + // make the chain definition output cp = CCinit(&CC, EVAL_PBAASDEFINITION); @@ -207,6 +216,15 @@ void GetDefinedChains(vector &chains, bool includeExpired } } +void CheckPBaaSAPIsValid() +{ + if (!chainActive.LastTip() || + CConstVerusSolutionVector::activationHeight.ActiveVersion(chainActive.LastTip()->GetHeight()) != CConstVerusSolutionVector::activationHeight.SOLUTION_VERUSV3) + { + throw JSONRPCError(RPC_INVALID_REQUEST, "PBaaS not activated on blockchain."); + } +} + UniValue getchaindefinition(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -250,6 +268,9 @@ UniValue getchaindefinition(const UniValue& params, bool fHelp) + HelpExampleRpc("getchaindefinition", "\"chainname\"") ); } + + CheckPBaaSAPIsValid(); + UniValue ret(UniValue::VOBJ); string name = params[0].get_str(); @@ -316,6 +337,9 @@ UniValue getdefinedchains(const UniValue& params, bool fHelp) + HelpExampleRpc("getdefinedchains", "true") ); } + + CheckPBaaSAPIsValid(); + UniValue ret(UniValue::VARR); bool includeExpired = params[0].isBool() ? params[0].get_bool() : false; @@ -694,6 +718,7 @@ UniValue submitnotarizationpayment(const UniValue& params, bool fHelp) + HelpExampleRpc("submitnotarizationpayment", "\"hextx\"") ); } + CheckPBaaSAPIsValid(); } @@ -719,6 +744,8 @@ UniValue submitacceptednotarization(const UniValue& params, bool fHelp) ); } + CheckPBaaSAPIsValid(); + // decode the transaction and ensure that it is formatted as expected CTransaction notarization; CPBaaSNotarization pbn; @@ -1402,6 +1429,134 @@ UniValue getcrossnotarization(const UniValue& params, bool fHelp) return ret; } +UniValue sendtochain(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + { + throw runtime_error( + "sendtochain '[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'\n" + "\nThis sends a Verus output as a JSON object or lists of Verus outputs as a list of objects to multiple chains or back.\n" + "\nFunds are sourced automatically from the current wallet, which must be present, as in sendtoaddress.\n" + + "\nArguments\n" + " {\n" + " \"chain\" : \"xxxx\", (string, required) unique Verus ecosystem-wide name/symbol of this PBaaS chain\n" + " \"paymentaddress\" : \"Rxxx\", (string, required) premine and launch fee recipient\n" + " \"amount\" : \"n\", (int64, required) amount of coins that will be premined and distributed to premine address\n" + " \"convert\" : \"false\", (bool, optional) auto-convert to PBaaS currency at current price\n" + " }\n" + + "\nResult:\n" + " \"txid\" : \"transactionid\" (string) The transaction id.\n" + + "\nExamples:\n" + + HelpExampleCli("sendtochain", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'") + + HelpExampleRpc("sendtochain", "'[{\"name\": \"PBAASCHAIN\", \"paymentaddress\": \"RRehdmUV7oEAqoZnzEGBH34XysnWaBatct\", \"amount\": 5.0}]'") + ); + } + + CheckPBaaSAPIsValid(); + + // each object represents a send, and all sends are aggregated into one transaction to improve potential for scaling when moving funds between + // and across multiple chains. + // + // each output will require an additional standard cross-chain fee that will be divided evenly in two ways, + // between the transaction aggregator -- the miner or staker who creates the aggregating export, + // and the transaction importer on the alternate chain who posts each exported bundle. + // + vector outputs; + vector vConvert; + + if (params.size() != 1 || (!params[0].isArray() && !params[0].isObject())) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters. Must provide a single object or single list of objects that represent valid outputs. see help."); + } + + const UniValue *pOutputArr = ¶ms[0]; + UniValue substArr(UniValue::VARR); + if (params[0].isObject()) + { + substArr.push_back(params[0]); + pOutputArr = &substArr; + } + const UniValue &objArr = *pOutputArr; + + // convert all entries to CRecipient + // any failure fails all + for (int i = 0; i < objArr.size(); i++) + { + // default double fee for miner of chain definition tx + // one output for definition, one for finalization + string name = uni_get_str(find_value(params[0], "chain"), ""); + string paymentAddr = uni_get_str(find_value(params[0], "paymentaddress"), ""); + CAmount amount = uni_get_int64(find_value(params[0], "amount"), -1); + bool convert = uni_get_int(find_value(params[0], "convert"), false); + + if (name == "" || paymentAddr == "" || amount < 0) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameters for object #" + to_string(i)); + } + + CBitcoinAddress ba(DecodeDestination(paymentAddr)); + CKeyID kID; + + if (!ba.IsValid() || !ba.GetKeyID(kID)) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid payment address in object #" + to_string(i)); + } + + uint160 chainID = CCrossChainRPCData::GetChainID(name); + CPBaaSChainDefinition chainDef; + // validate that the target chain is still running + if (!GetChainDefinition(chainID, chainDef) || !chainDef.IsValid()) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain specified in object #" + to_string(i) + " is not a valid chain"); + } + + // validate that the entry is a valid chain being notarized + CChainNotarizationData nData; + if (GetNotarizationData(chainID, IsVerusActive() ? EVAL_ACCEPTEDNOTARIZATION : EVAL_ACCEPTEDNOTARIZATION, nData)) + { + // if the chain is being notarized and cannot confirm before its end, refuse to send + // also, if it hasn't been notarized as recently as the active notarization threshold, refuse as well + // TODO: define threshold, for now, only check that last notarization is at least minimum confirmation + // distance + if ((nData.vtx.size() && + (nData.vtx[nData.bestChain].second.notarizationHeight + (CPBaaSNotarization::MIN_BLOCKS_BETWEEN_ACCEPTED * CPBaaSNotarization::FINAL_CONFIRMATIONS) > + chainDef.endBlock)) || + (!chainDef.eraOptions.size() || !(chainDef.eraOptions[0] & CPBaaSChainDefinition::OPTION_RESERVE))) + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain specified in object #" + to_string(i) + " is not a valid chain"); + } + } + else + { + throw JSONRPCError(RPC_INVALID_PARAMETER, "Chain specified in object #" + to_string(i) + " is not notarized"); + } + + // make the output script, either a normal output or a conversion + + CCcontract_info CC; + CCcontract_info *cp; + cp = CCinit(&CC, EVAL_CROSSCHAIN_INPUT); + + CPubKey pk = CPubKey(ParseHex(CC.CChexstr)); + // TODO: determine dests properly + std::vector dests = std::vector({CKeyID(chainDef.GetConditionID(EVAL_CROSSCHAIN_INPUT))}); + CCrossChainInput cci; // TODO fill with payment script and amount (adjust amount for fees) + CTxOut ccOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_INPUT, amount, pk, dests, cci); + outputs.push_back(CRecipient({ccOut.scriptPubKey, amount, false})); + } + // send the specified amount to chain ID as an EVAL_CROSSCHAIN_INPUT to the chain ID + // the transaction holds the ultimate destination address, and until the transaction + // is packaged into an EVAL_CROSSCHAIN_EXPORT bundle, the output can be spent by + // the original sender + // once bundled, transaction outputs can be transferred to the other chain through a proof of the bundle by anyone and is considered irreversible + // all bundled outputs can be moved to and spent on the destination chain as soon as a notarization of the same block + // or later has been confirmed. bundling transactions can be done at any time, but moving an export bundle + // happens only after is is in a block behind a confirmed notarization. +} + UniValue definechain(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) @@ -1446,6 +1601,9 @@ UniValue definechain(const UniValue& params, bool fHelp) + HelpExampleRpc("definechain", "jsondefinition") ); } + + CheckPBaaSAPIsValid(); + if (!params[0].isObject()) { throw JSONRPCError(RPC_INVALID_PARAMETER, "JSON object required. see help."); @@ -1616,6 +1774,8 @@ UniValue addmergedblock(const UniValue& params, bool fHelp) ); } + CheckPBaaSAPIsValid(); + // check to see if we should replace any existing block or add a new one. if so, add this to the merge mine vector string name = params[1].get_str(); if (name == "") @@ -1689,6 +1849,8 @@ UniValue submitmergedblock(const UniValue& params, bool fHelp) + HelpExampleRpc("submitblock", "\"mydata\"") ); + CheckPBaaSAPIsValid(); + CBlock block; //LogPrintStr("Hex block submission: " + params[0].get_str()); if (!DecodeHexBlk(block, params[0].get_str())) @@ -1797,6 +1959,8 @@ UniValue getmergedblocktemplate(const UniValue& params, bool fHelp) + HelpExampleRpc("getblocktemplate", "") ); + CheckPBaaSAPIsValid(); + LOCK(cs_main); // Wallet or miner address is required because we support coinbasetxn diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index eecfab83c9c..51391782c20 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -20,6 +20,11 @@ #include "script/sign.h" #include "script/standard.h" #include "uint256.h" + +#include "cc/CCinclude.h" +#include "cc/eval.h" +#include "pbaas/notarization.h" + #ifdef ENABLE_WALLET #include "wallet/wallet.h" #endif @@ -334,6 +339,65 @@ void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) ScriptPubKeyToJSON(txout.scriptPubKey, o, true); out.push_back(Pair("scriptPubKey", o)); vout.push_back(out); + COptCCParams p; + if (IsPayToCryptoCondition(txout.scriptPubKey, p) && p.version >= COptCCParams::VERSION_V2) + { + switch(p.evalCode) + { + case EVAL_PBAASDEFINITION: + { + CPBaaSChainDefinition definition; + + if (p.vData.size() && (definition = CPBaaSChainDefinition(p.vData[0])).IsValid()) + { + out.push_back(Pair("pbaasChainDefinition", definition.ToUniValue())); + } + break; + } + + case EVAL_SERVICEREWARD: + { + CServiceReward reward; + + if (p.vData.size() && (reward = CServiceReward(p.vData[0])).IsValid()) + { + out.push_back(Pair("pbaasServiceReward", reward.ToUniValue())); + } + break; + } + + case EVAL_EARNEDNOTARIZATION: + case EVAL_ACCEPTEDNOTARIZATION: + { + CPBaaSNotarization notarization; + + if (p.vData.size() && (notarization = CPBaaSNotarization(p.vData[0])).IsValid()) + { + out.push_back(Pair("pbaasNotarization", notarization.ToUniValue())); + } + break; + } + + case EVAL_FINALIZENOTARIZATION: + { + CNotarizationFinalization finalization; + + if (p.vData.size() && (finalization = CNotarizationFinalization(p.vData[0])).IsValid()) + { + out.push_back(Pair("pbaasFinalization", finalization.ToUniValue())); + } + break; + } + + case EVAL_INSTANTSPEND: + case EVAL_CROSSCHAIN_INPUT: + case EVAL_CROSSCHAIN_OUTPUT: + case EVAL_CROSSCHAIN_EXPORT: + case EVAL_CROSSCHAIN_IMPORT: + case EVAL_STAKEGUARD: + break; + } + } } entry.push_back(Pair("vout", vout));