2
.gitignore
vendored
2
.gitignore
vendored
@@ -155,3 +155,5 @@ src/rogue.scr
|
||||
|
||||
src/cc/rogue/confdefs.h
|
||||
src/cc/rogue/x64
|
||||
|
||||
src/cc/dapps/a.out
|
||||
|
||||
@@ -491,7 +491,7 @@ libbitcoin_common_a_SOURCES = \
|
||||
script/sign.cpp \
|
||||
script/standard.cpp \
|
||||
transaction_builder.cpp \
|
||||
cc/CCtokensOpRet.cpp \
|
||||
cc/CCtokenutils.cpp \
|
||||
cc/CCutilbits.cpp \
|
||||
$(BITCOIN_CORE_H) \
|
||||
$(LIBZCASH_H)
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
bool ImportGatewayValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &tx, uint32_t nIn);
|
||||
bool ImportGatewayExactAmounts(bool goDeeper, struct CCcontract_info *cpTokens, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 tokenid);
|
||||
std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> pubkeys,uint8_t p1,uint8_t p2,uint8_t p3,uint8_t p4);
|
||||
std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t burnvout,std::string rawburntx,std::vector<uint8_t>proof,CPubKey destpub);
|
||||
std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t burnvout,std::string rawburntx,std::vector<uint8_t>proof,CPubKey destpub,int64_t amount);
|
||||
std::string ImportGatewayWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,CPubKey withdrawpub,int64_t amount);
|
||||
std::string ImportGatewayPartialSign(uint64_t txfee,uint256 lasttxid,std::string refcoin, std::string hex);
|
||||
std::string ImportGatewayCompleteSigning(uint64_t txfee,uint256 lasttxid,std::string refcoin,std::string hex);
|
||||
|
||||
@@ -206,13 +206,13 @@ int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex);
|
||||
|
||||
CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, vscript_t vopretNonfungible);
|
||||
CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey, std::string name, std::string description, std::vector<std::pair<uint8_t, vscript_t>> oprets);
|
||||
CScript EncodeTokenImportOpRet(std::vector<uint8_t> origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector<std::pair<uint8_t, vscript_t>> oprets);
|
||||
|
||||
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::pair<uint8_t, vscript_t> opretWithId);
|
||||
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> oprets);
|
||||
int64_t AddCClibtxfee(struct CCcontract_info *cp,CMutableTransaction &mtx,CPubKey pk);
|
||||
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description);
|
||||
uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
|
||||
uint8_t DecodeTokenImportOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
|
||||
|
||||
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> &oprets);
|
||||
void GetNonfungibleData(uint256 tokenid, vscript_t &vopretNonfungible);
|
||||
bool ExtractTokensCCVinPubkeys(const CTransaction &tx, std::vector<CPubKey> &vinPubkeys);
|
||||
@@ -293,6 +293,8 @@ void vcalc_sha256(char deprecated[(256 >> 3) * 2 + 1],uint8_t hash[256 >> 3],uin
|
||||
bits256 bits256_doublesha256(char *deprecated,uint8_t *data,int32_t datalen);
|
||||
UniValue ValueFromAmount(const CAmount& amount);
|
||||
|
||||
int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey);
|
||||
int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey);
|
||||
|
||||
// bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream:
|
||||
#define CCLOG_INFO 0
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2018 The SuperNET Developers. *
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
@@ -14,6 +14,7 @@
|
||||
******************************************************************************/
|
||||
|
||||
#include "CCtokens.h"
|
||||
#include "importcoin.h"
|
||||
|
||||
/* TODO: correct this:
|
||||
-----------------------------
|
||||
@@ -102,22 +103,21 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
|
||||
|
||||
switch (funcid)
|
||||
{
|
||||
case 'c': // create wont be called to be verified as it has no CC inputs
|
||||
case 'c': // token create should not be validated as it has no CC inputs, so return 'invalid'
|
||||
// token tx structure for 'c':
|
||||
//vin.0: normal input
|
||||
//vout.0: issuance tokenoshis to CC
|
||||
//vout.1: normal output for change (if any)
|
||||
//vout.n-1: opreturn EVAL_TOKENS 'c' <tokenname> <description>
|
||||
//if (evalCodeInOpret != EVAL_TOKENS)
|
||||
// return eval->Invalid("unexpected TokenValidate for createtoken");
|
||||
//else
|
||||
return true;
|
||||
return eval->Invalid("incorrect token funcid");
|
||||
|
||||
case 't': // transfer
|
||||
// token tx structure for 't'
|
||||
//vin.0: normal input
|
||||
//vin.1 .. vin.n-1: valid CC outputs
|
||||
//vout.0 to n-2: tokenoshis output to CC
|
||||
//vout.n-2: normal output for change (if any)
|
||||
//vout.n-1: opreturn <other evalcode> 't' tokenid <other contract payload>
|
||||
//vout.n-1: opreturn EVAL_TOKENS 't' tokenid <other contract payload>
|
||||
if (inputs == 0)
|
||||
return eval->Invalid("no token inputs for transfer");
|
||||
|
||||
@@ -129,20 +129,7 @@ bool TokensValidate(struct CCcontract_info *cp, Eval* eval, const CTransaction &
|
||||
return eval->Invalid("unexpected token funcid");
|
||||
}
|
||||
|
||||
// forward validation if evalcode in opret is not EVAL_TOKENS
|
||||
// init for forwarding validation call
|
||||
//if (evalCodeInOpret != EVAL_TOKENS) { // TODO: should we check also only allowed for tokens evalcodes, like EVAL_ASSETS, EVAL_GATEWAYS?
|
||||
// struct CCcontract_info *cpOther = NULL, C;
|
||||
|
||||
// cpOther = CCinit(&C, evalCodeInOpret);
|
||||
// if (cpOther)
|
||||
// return cpOther->validate(cpOther, eval, tx, nIn);
|
||||
// else
|
||||
// return eval->Invalid("unsupported evalcode in opret");
|
||||
//}
|
||||
return true;
|
||||
// what does this do?
|
||||
// return(PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts));
|
||||
}
|
||||
|
||||
// helper funcs:
|
||||
@@ -333,7 +320,8 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
vscript_t vopretExtra, vopretNonfungible;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
uint8_t evalCode = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim
|
||||
uint8_t evalCodeNonfungible = 0;
|
||||
uint8_t evalCode1 = EVAL_TOKENS; // if both payloads are empty maybe it is a transfer to non-payload-one-eval-token vout like GatewaysClaim
|
||||
uint8_t evalCode2 = 0; // will be checked if zero or not
|
||||
|
||||
// test vouts for possible token use-cases:
|
||||
@@ -354,12 +342,12 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
// non-fungible-eval -> EVAL_TOKENS -> assets-eval
|
||||
|
||||
if (vopretNonfungible.size() > 0)
|
||||
evalCode = vopretNonfungible.begin()[0];
|
||||
evalCodeNonfungible = evalCode1 = vopretNonfungible.begin()[0];
|
||||
if (vopretExtra.size() > 0)
|
||||
evalCode2 = vopretExtra.begin()[0];
|
||||
|
||||
if (evalCode == EVAL_TOKENS && evalCode2 != 0) {
|
||||
evalCode = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...)
|
||||
if (evalCode1 == EVAL_TOKENS && evalCode2 != 0) {
|
||||
evalCode1 = evalCode2; // for using MakeTokensCC1vout(evalcode,...) instead of MakeCC1vout(EVAL_TOKENS, evalcode...)
|
||||
evalCode2 = 0;
|
||||
}
|
||||
|
||||
@@ -369,39 +357,41 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
// maybe this is dual-eval 1 pubkey or 1of2 pubkey vout?
|
||||
if (voutPubkeys.size() >= 1 && voutPubkeys.size() <= 2) {
|
||||
// check dual/three-eval 1 pubkey vout with the first pubkey
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) );
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0]")) );
|
||||
if (evalCode2 != 0)
|
||||
// also check in backward evalcode order
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) );
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("three-eval cc1 pk[0] backward-eval")) );
|
||||
|
||||
if(voutPubkeys.size() == 2) {
|
||||
// check dual/three eval 1of2 pubkeys vout
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) );
|
||||
testVouts.push_back( std::make_pair(MakeTokensCC1of2vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2")) );
|
||||
// check dual/three eval 1 pubkey vout with the second pubkey
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1]")));
|
||||
if (evalCode2 != 0) {
|
||||
// also check in backward evalcode order:
|
||||
// check dual/three eval 1of2 pubkeys vout
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2 backward-eval")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1of2vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[0], voutPubkeys[1]), std::string("three-eval cc1of2 backward-eval")));
|
||||
// check dual/three eval 1 pubkey vout with the second pubkey
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("three-eval cc1 pk[1] backward-eval")));
|
||||
}
|
||||
}
|
||||
|
||||
// maybe this is like gatewayclaim to single-eval token?
|
||||
if( evalCodeNonfungible == 0 ) // do not allow to convert non-fungible to fungible token
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]")));
|
||||
|
||||
// maybe this is like gatewayclaim to single-eval token?
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[0]), std::string("single-eval cc1 pk[0]")));
|
||||
// maybe this is like FillSell for non-fungible token?
|
||||
if( evalCode != 0 )
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval-token cc1 pk[0]")));
|
||||
if( evalCode1 != 0 )
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval-token cc1 pk[0]")));
|
||||
if( evalCode2 != 0 )
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[0]), std::string("dual-eval2-token cc1 pk[0]")));
|
||||
|
||||
// the same for pk[1]:
|
||||
if (voutPubkeys.size() == 2) {
|
||||
// the same for pk[1]:
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]")));
|
||||
if (evalCode != 0)
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]")));
|
||||
if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, voutPubkeys[1]), std::string("single-eval cc1 pk[1]")));
|
||||
if (evalCode1 != 0)
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval-token cc1 pk[1]")));
|
||||
if (evalCode2 != 0)
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, tx.vout[v].nValue, voutPubkeys[1]), std::string("dual-eval2-token cc1 pk[1]")));
|
||||
}
|
||||
@@ -413,52 +403,95 @@ int64_t IsTokensvout(bool goDeeper, bool checkPubkeys /*<--not used, always true
|
||||
FilterOutTokensUnspendablePk(vinPubkeysUnfiltered, vinPubkeys); // cannot send tokens to token unspendable cc addr (only marker is allowed there)
|
||||
|
||||
for(std::vector<CPubKey>::iterator it = vinPubkeys.begin(); it != vinPubkeys.end(); it++) {
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk")));
|
||||
if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, *it), std::string("single-eval cc1 self vin pk")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, evalCode2, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk")));
|
||||
|
||||
if (evalCode2 != 0)
|
||||
// also check in backward evalcode order:
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval")));
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode2, evalCode1, tx.vout[v].nValue, *it), std::string("three-eval cc1 self vin pk backward-eval")));
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
CPubKey origPubkey;
|
||||
vscript_t vorigPubkey;
|
||||
std::string dummyName, dummyDescription;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) {
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
|
||||
return 0;
|
||||
// try all test vouts:
|
||||
for (auto t : testVouts) {
|
||||
if (t.first == tx.vout[v]) { // test vout matches
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl);
|
||||
return tx.vout[v].nValue;
|
||||
}
|
||||
}
|
||||
|
||||
origPubkey = pubkey2pk(vorigPubkey);
|
||||
|
||||
// for 'c' recognize the tokens only to token originator pubkey (but not to unspendable <-- closed sec violation)
|
||||
// maybe this is like gatewayclaim to single-eval token?
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk")));
|
||||
// maybe this is like FillSell for non-fungible token?
|
||||
if (evalCode != 0)
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk")));
|
||||
}
|
||||
else { // funcid == 'c'
|
||||
|
||||
if (!tx.IsCoinImport()) {
|
||||
|
||||
// try all test vouts:
|
||||
for (auto t : testVouts) {
|
||||
if (t.first == tx.vout[v]) {
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() valid amount=" << tx.vout[v].nValue << " msg=" << t.second << " evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " txid=" << tx.GetHash().GetHex() << " tokenid=" << reftokenid.GetHex() << std::endl);
|
||||
return tx.vout[v].nValue;
|
||||
vscript_t vorigPubkey;
|
||||
std::string dummyName, dummyDescription;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
if (DecodeTokenCreateOpRet(tx.vout.back().scriptPubKey, vorigPubkey, dummyName, dummyDescription, oprets) == 0) {
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() could not decode create opret" << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
CPubKey origPubkey = pubkey2pk(vorigPubkey);
|
||||
|
||||
|
||||
// TODO: add voutPubkeys for 'c' tx
|
||||
|
||||
/* this would not work for imported tokens:
|
||||
// for 'c' recognize the tokens only to token originator pubkey (but not to unspendable <-- closed sec violation)
|
||||
// maybe this is like gatewayclaim to single-eval token?
|
||||
if (evalCodeNonfungible == 0) // do not allow to convert non-fungible to fungible token
|
||||
testVouts.push_back(std::make_pair(MakeCC1vout(EVAL_TOKENS, tx.vout[v].nValue, origPubkey), std::string("single-eval cc1 orig-pk")));
|
||||
// maybe this is like FillSell for non-fungible token?
|
||||
if (evalCode1 != 0)
|
||||
testVouts.push_back(std::make_pair(MakeTokensCC1vout(evalCode1, tx.vout[v].nValue, origPubkey), std::string("dual-eval-token cc1 orig-pk"))); */
|
||||
|
||||
// note: this would not work if there are several pubkeys in the tokencreator's wallet (AddNormalinputs does not use pubkey param):
|
||||
// for tokenbase tx check that normal inputs sent from origpubkey > cc outputs
|
||||
int64_t ccOutputs = 0;
|
||||
for (auto vout : tx.vout)
|
||||
if (vout.scriptPubKey.IsPayToCryptoCondition() //TODO: add voutPubkey validation
|
||||
&& !IsTokenMarkerVout(vout)) // should not be marker here
|
||||
ccOutputs += vout.nValue;
|
||||
|
||||
int64_t normalInputs = TotalPubkeyNormalInputs(tx, origPubkey); // check if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey)
|
||||
LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << " for tokenbase=" << reftokenid.GetHex() << std::endl);
|
||||
|
||||
if (normalInputs >= ccOutputs) {
|
||||
LOGSTREAM("cctokens", CCLOG_DEBUG2, stream << indentStr << "IsTokensvout() assured normalInputs >= ccOutputs" << " for tokenbase=" << reftokenid.GetHex() << std::endl);
|
||||
if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker
|
||||
return tx.vout[v].nValue;
|
||||
else
|
||||
return 0; // vout is good, but do not take marker into account
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("cctokens", CCLOG_INFO, stream << indentStr << "IsTokensvout() skipping vout not fulfilled normalInputs >= ccOutput" << " for tokenbase=" << reftokenid.GetHex() << " normalInputs=" << normalInputs << " ccOutputs=" << ccOutputs << std::endl);
|
||||
}
|
||||
}
|
||||
}
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
|
||||
else {
|
||||
// imported tokens are checked in the eval::ImportCoin() validation code
|
||||
if (!IsTokenMarkerVout(tx.vout[v])) // exclude marker
|
||||
return tx.vout[v].nValue;
|
||||
else
|
||||
return 0; // vout is good, but do not take marker into account
|
||||
}
|
||||
}
|
||||
LOGSTREAM("cctokens", CCLOG_DEBUG1, stream << indentStr << "IsTokensvout() no valid vouts evalCode=" << (int)evalCode1 << " evalCode2=" << (int)evalCode2 << " for txid=" << tx.GetHash().GetHex() << " for tokenid=" << reftokenid.GetHex() << std::endl);
|
||||
}
|
||||
|
||||
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() CC vout v.%d of n=%d amount=%.8f txid=%s\n",v,n,(double)0/COIN, tx.GetHash().GetHex().c_str());
|
||||
}
|
||||
//std::cerr << indentStr; fprintf(stderr,"IsTokensvout() normal output v.%d %.8f\n",v,(double)tx.vout[v].nValue/COIN);
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool IsTokenMarkerVout(CTxOut vout) {
|
||||
struct CCcontract_info *cpTokens, CCtokens_info;
|
||||
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
|
||||
return vout == MakeCC1vout(EVAL_TOKENS, vout.nValue, GetUnspendable(cpTokens, NULL));
|
||||
}
|
||||
|
||||
// compares cc inputs vs cc outputs (to prevent feeding vouts from normal inputs)
|
||||
bool TokensExactAmounts(bool goDeeper, struct CCcontract_info *cp, int64_t &inputs, int64_t &outputs, Eval* eval, const CTransaction &tx, uint256 reftokenid)
|
||||
{
|
||||
@@ -582,6 +615,7 @@ int64_t AddTokenCCInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, C
|
||||
GetTokensCCaddress(cp, tokenaddr, pk);
|
||||
SetCCunspents(unspentOutputs, tokenaddr,true);
|
||||
|
||||
|
||||
if (unspentOutputs.empty()) {
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "AddTokenCCInputs() no utxos for token dual/three eval addr=" << tokenaddr << " evalcode=" << (int)cp->evalcode << " additionalTokensEvalcode2=" << (int)cp->additionalTokensEvalcode2 << std::endl);
|
||||
}
|
||||
@@ -747,7 +781,7 @@ CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey) {
|
||||
return CPubKey(); //return invalid pubkey
|
||||
}
|
||||
|
||||
|
||||
// returns token creation signed raw tx
|
||||
std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, std::string description, vscript_t nonfungibleData)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
@@ -767,7 +801,7 @@ std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, st
|
||||
cp = CCinit(&C, EVAL_TOKENS);
|
||||
if (name.size() > 32 || description.size() > 4096) // this is also checked on rpc level
|
||||
{
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl);
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG1, stream << "name len=" << name.size() << " or description len=" << description.size() << " is too big" << std::endl);
|
||||
CCerror = "name should be <= 32, description should be <= 4096";
|
||||
return("");
|
||||
}
|
||||
@@ -777,6 +811,13 @@ std::string CreateToken(int64_t txfee, int64_t tokensupply, std::string name, st
|
||||
|
||||
if (AddNormalinputs(mtx, mypk, tokensupply + 2 * txfee, 64) > 0)
|
||||
{
|
||||
|
||||
int64_t mypkInputs = TotalPubkeyNormalInputs(mtx, mypk);
|
||||
if (mypkInputs < tokensupply) { // check that tokens amount are really issued with mypk (because in the wallet there maybe other privkeys)
|
||||
CCerror = "some inputs signed not with -pubkey=pk";
|
||||
return std::string("");
|
||||
}
|
||||
|
||||
uint8_t destEvalCode = EVAL_TOKENS;
|
||||
if( nonfungibleData.size() > 0 )
|
||||
destEvalCode = nonfungibleData.begin()[0];
|
||||
@@ -843,7 +884,7 @@ std::string TokenTransfer(int64_t txfee, uint256 tokenid, vscript_t destpubkey,
|
||||
}
|
||||
else {
|
||||
CCerror = strprintf("no token inputs");
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << total << std::endl);
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenTransfer() " << CCerror << " for amount=" << total << std::endl);
|
||||
}
|
||||
//} else fprintf(stderr,"numoutputs.%d != numamounts.%d\n",n,(int32_t)amounts.size());
|
||||
}
|
||||
@@ -880,7 +921,7 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ);
|
||||
uint256 hashBlock;
|
||||
CTransaction vintx;
|
||||
CTransaction tokenbaseTx;
|
||||
std::vector<uint8_t> origpubkey;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
vscript_t vopretNonfungible;
|
||||
@@ -889,14 +930,14 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
|
||||
cpTokens = CCinit(&tokensCCinfo, EVAL_TOKENS);
|
||||
|
||||
if( !GetTransaction(tokenid, vintx, hashBlock, false) )
|
||||
if( !GetTransaction(tokenid, tokenbaseTx, hashBlock, false) )
|
||||
{
|
||||
fprintf(stderr, "TokenInfo() cant find tokenid\n");
|
||||
result.push_back(Pair("result", "error"));
|
||||
result.push_back(Pair("error", "cant find tokenid"));
|
||||
return(result);
|
||||
}
|
||||
if (vintx.vout.size() > 0 && DecodeTokenCreateOpRet(vintx.vout[vintx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c')
|
||||
if (tokenbaseTx.vout.size() > 0 && DecodeTokenCreateOpRet(tokenbaseTx.vout[tokenbaseTx.vout.size() - 1].scriptPubKey, origpubkey, name, description, oprets) != 'c')
|
||||
{
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "TokenInfo() passed tokenid isnt token creation txid" << std::endl);
|
||||
result.push_back(Pair("result", "error"));
|
||||
@@ -909,8 +950,8 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
result.push_back(Pair("name", name));
|
||||
|
||||
int64_t supply = 0, output;
|
||||
for (int v = 0; v < vintx.vout.size() - 1; v++)
|
||||
if ((output = IsTokensvout(false, true, cpTokens, NULL, vintx, v, tokenid)) > 0)
|
||||
for (int v = 0; v < tokenbaseTx.vout.size() - 1; v++)
|
||||
if ((output = IsTokensvout(false, true, cpTokens, NULL, tokenbaseTx, v, tokenid)) > 0)
|
||||
supply += output;
|
||||
result.push_back(Pair("supply", supply));
|
||||
result.push_back(Pair("description", description));
|
||||
@@ -919,6 +960,40 @@ UniValue TokenInfo(uint256 tokenid)
|
||||
if( !vopretNonfungible.empty() )
|
||||
result.push_back(Pair("data", HexStr(vopretNonfungible)));
|
||||
|
||||
if (tokenbaseTx.IsCoinImport()) { // if imported token
|
||||
ImportProof proof;
|
||||
CTransaction burnTx;
|
||||
std::vector<CTxOut> payouts;
|
||||
CTxDestination importaddress;
|
||||
|
||||
std::string sourceSymbol = "can't decode";
|
||||
std::string sourceTokenId = "can't decode";
|
||||
|
||||
if (UnmarshalImportTx(tokenbaseTx, proof, burnTx, payouts))
|
||||
{
|
||||
// extract op_return to get burn source chain.
|
||||
std::vector<uint8_t> burnOpret;
|
||||
std::string targetSymbol;
|
||||
uint32_t targetCCid;
|
||||
uint256 payoutsHash;
|
||||
std::vector<uint8_t> rawproof;
|
||||
if (UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof)) {
|
||||
if (rawproof.size() > 0) {
|
||||
CTransaction tokenbasetx;
|
||||
E_UNMARSHAL(rawproof, ss >> sourceSymbol;
|
||||
if (!ss.eof())
|
||||
ss >> tokenbasetx);
|
||||
|
||||
if (!tokenbasetx.IsNull())
|
||||
sourceTokenId = tokenbasetx.GetHash().GetHex();
|
||||
}
|
||||
}
|
||||
}
|
||||
result.push_back(Pair("IsImported", "yes"));
|
||||
result.push_back(Pair("sourceChain", sourceSymbol));
|
||||
result.push_back(Pair("sourceTokenId", sourceTokenId));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ std::string CreateToken(int64_t txfee, int64_t assetsupply, std::string name, st
|
||||
std::string TokenTransfer(int64_t txfee, uint256 assetid, std::vector<uint8_t> destpubkey, int64_t total);
|
||||
int64_t HasBurnedTokensvouts(struct CCcontract_info *cp, Eval* eval, const CTransaction& tx, uint256 reftokenid);
|
||||
CPubKey GetTokenOriginatorPubKey(CScript scriptPubKey);
|
||||
bool IsTokenMarkerVout(CTxOut vout);
|
||||
|
||||
int64_t GetTokenBalance(CPubKey pk, uint256 tokenid);
|
||||
UniValue TokenInfo(uint256 tokenid);
|
||||
|
||||
@@ -1,5 +1,21 @@
|
||||
/******************************************************************************
|
||||
* Copyright © 2014-2019 The SuperNET Developers. *
|
||||
* *
|
||||
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
||||
* the top-level directory of this distribution for the individual copyright *
|
||||
* holder information and the developer policies on copyright and licensing. *
|
||||
* *
|
||||
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
||||
* SuperNET software, including this file may be copied, modified, propagated *
|
||||
* or distributed except according to the terms contained in the LICENSE file *
|
||||
* *
|
||||
* Removal or modification of this copyright notice is prohibited. *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
// encode decode tokens opret
|
||||
// (moved to a separate file to enable linking lib common.so with importcoin.cpp)
|
||||
// make token cryptoconditions and vouts
|
||||
// This code was moved to a separate source file to enable linking libcommon.so (with importcoin.cpp which depends on some token functions)
|
||||
|
||||
#include "CCtokens.h"
|
||||
|
||||
@@ -44,6 +60,7 @@ CScript EncodeTokenCreateOpRet(uint8_t funcid, std::vector<uint8_t> origpubkey,
|
||||
return(opret);
|
||||
}
|
||||
|
||||
/*
|
||||
// opret 'i' for imported tokens
|
||||
CScript EncodeTokenImportOpRet(std::vector<uint8_t> origpubkey, std::string name, std::string description, uint256 srctokenid, std::vector<std::pair<uint8_t, vscript_t>> oprets)
|
||||
{
|
||||
@@ -62,7 +79,7 @@ CScript EncodeTokenImportOpRet(std::vector<uint8_t> origpubkey, std::string name
|
||||
});
|
||||
return(opret);
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
|
||||
CScript EncodeTokenOpRet(uint256 tokenid, std::vector<CPubKey> voutPubkeys, std::pair<uint8_t, vscript_t> opretWithId)
|
||||
@@ -158,37 +175,9 @@ uint8_t DecodeTokenCreateOpRet(const CScript &scriptPubKey, std::vector<uint8_t>
|
||||
return (uint8_t)0;
|
||||
}
|
||||
|
||||
// for imported tokens
|
||||
uint8_t DecodeTokenImportOpRet(const CScript &scriptPubKey, std::vector<uint8_t> &origpubkey, std::string &name, std::string &description, uint256 &srctokenid, std::vector<std::pair<uint8_t, vscript_t>> &oprets)
|
||||
{
|
||||
vscript_t vopret, vblob;
|
||||
uint8_t dummyEvalcode, funcid, opretId = 0;
|
||||
|
||||
GetOpReturnData(scriptPubKey, vopret);
|
||||
oprets.clear();
|
||||
|
||||
if (vopret.size() > 2 && vopret.begin()[0] == EVAL_TOKENS && vopret.begin()[1] == 'i')
|
||||
{
|
||||
if (E_UNMARSHAL(vopret, ss >> dummyEvalcode; ss >> funcid; ss >> origpubkey; ss >> name; ss >> description; ss >> srctokenid;
|
||||
while (!ss.eof()) {
|
||||
ss >> opretId;
|
||||
if (!ss.eof()) {
|
||||
ss >> vblob;
|
||||
oprets.push_back(std::make_pair(opretId, vblob));
|
||||
}
|
||||
}))
|
||||
{
|
||||
srctokenid = revuint256(srctokenid); // do not forget this
|
||||
return(funcid);
|
||||
}
|
||||
}
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenImportOpRet() incorrect token import opret" << std::endl);
|
||||
return (uint8_t)0;
|
||||
}
|
||||
|
||||
// decodes token opret:
|
||||
// decode token opret:
|
||||
// for 't' returns all data from opret, vopretExtra contains other contract's data (currently only assets').
|
||||
// for 'c' and 'i' returns only funcid. NOTE: nonfungible data is not returned
|
||||
// for 'c' returns only funcid. NOTE: nonfungible data is not returned
|
||||
uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, uint256 &tokenid, std::vector<CPubKey> &voutPubkeys, std::vector<std::pair<uint8_t, vscript_t>> &oprets)
|
||||
{
|
||||
vscript_t vopret, vblob, dummyPubkey, vnonfungibleDummy;
|
||||
@@ -207,9 +196,6 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
|
||||
|
||||
if (script != NULL && vopret.size() > 2)
|
||||
{
|
||||
// NOTE: if parse error occures, parse might not be able to set error. It is safer to treat that it was eof if it is not set!
|
||||
// bool isEof = true;
|
||||
|
||||
evalCodeTokens = script[0];
|
||||
if (evalCodeTokens != EVAL_TOKENS) {
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_INFO, stream << "DecodeTokenOpRet() incorrect evalcode in tokens opret" << std::endl);
|
||||
@@ -217,15 +203,13 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
|
||||
}
|
||||
|
||||
funcId = script[1];
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl);
|
||||
LOGSTREAM((char *)"cctokens", CCLOG_DEBUG2, stream << "DecodeTokenOpRet() decoded funcId=" << (char)(funcId ? funcId : ' ') << std::endl);
|
||||
|
||||
switch (funcId)
|
||||
{
|
||||
case 'c':
|
||||
return DecodeTokenCreateOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, oprets);
|
||||
case 'i':
|
||||
return DecodeTokenImportOpRet(scriptPubKey, dummyPubkey, dummyName, dummyDescription, dummySrcTokenId, oprets);
|
||||
//break;
|
||||
|
||||
case 't':
|
||||
|
||||
// compatibility with old-style rogue or assets data (with no opretid):
|
||||
@@ -293,4 +277,75 @@ uint8_t DecodeTokenOpRet(const CScript scriptPubKey, uint8_t &evalCodeTokens, ui
|
||||
}
|
||||
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
// make 1of2 sigs cond
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk1));
|
||||
pks.push_back(CCNewSecp256k1(pk2));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
|
||||
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if (evalcode2 != 0)
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
|
||||
{
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
|
||||
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if (evalcode2 != 0)
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // signature
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) {
|
||||
return MakeTokensCCcond1(evalcode, 0, pk);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) {
|
||||
return MakeTokensCC1vout(evalcode, 0, nValue, pk);
|
||||
}
|
||||
|
||||
@@ -93,79 +93,6 @@ CTxOut MakeCC1of2vout(uint8_t evalcode,CAmount nValue,CPubKey pk1,CPubKey pk2, c
|
||||
return(vout);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, uint8_t evalcode2, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
// make 1of2 sigs cond
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk1));
|
||||
pks.push_back(CCNewSecp256k1(pk2));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back( CCNewEval(E_MARSHAL(ss << evalcode)) );
|
||||
if( evalcode != EVAL_TOKENS ) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1of2()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if( evalcode2 != 0 )
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // this is 1 of 2 sigs cc
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cryptocondition:
|
||||
CC *MakeTokensCCcond1of2(uint8_t evalcode, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCCcond1of2(evalcode, 0, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, uint8_t evalcode2, CPubKey pk)
|
||||
{
|
||||
std::vector<CC*> pks;
|
||||
pks.push_back(CCNewSecp256k1(pk));
|
||||
|
||||
std::vector<CC*> thresholds;
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode)));
|
||||
if (evalcode != EVAL_TOKENS) // if evalCode == EVAL_TOKENS, it is actually MakeCCcond1()!
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << (uint8_t)EVAL_TOKENS))); // this is eval token cc
|
||||
if (evalcode2 != 0)
|
||||
thresholds.push_back(CCNewEval(E_MARSHAL(ss << evalcode2))); // add optional additional evalcode
|
||||
thresholds.push_back(CCNewThreshold(1, pks)); // signature
|
||||
|
||||
return CCNewThreshold(thresholds.size(), thresholds);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cryptocondition:
|
||||
CC *MakeTokensCCcond1(uint8_t evalcode, CPubKey pk) {
|
||||
return MakeTokensCCcond1(evalcode, 0, pk);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk1, CPubKey pk2)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1of2(evalcode, evalcode2, pk1, pk2);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) 1of2 cc vout:
|
||||
CTxOut MakeTokensCC1of2vout(uint8_t evalcode, CAmount nValue, CPubKey pk1, CPubKey pk2) {
|
||||
return MakeTokensCC1of2vout(evalcode, 0, nValue, pk1, pk2);
|
||||
}
|
||||
|
||||
// make three-eval (token+evalcode+evalcode2) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, uint8_t evalcode2, CAmount nValue, CPubKey pk)
|
||||
{
|
||||
CTxOut vout;
|
||||
CC *payoutCond = MakeTokensCCcond1(evalcode, evalcode2, pk);
|
||||
vout = CTxOut(nValue, CCPubKey(payoutCond));
|
||||
cc_free(payoutCond);
|
||||
return(vout);
|
||||
}
|
||||
// overload to make two-eval (token+evalcode) cc vout:
|
||||
CTxOut MakeTokensCC1vout(uint8_t evalcode, CAmount nValue, CPubKey pk) {
|
||||
return MakeTokensCC1vout(evalcode, 0, nValue, pk);
|
||||
}
|
||||
|
||||
|
||||
CC* GetCryptoCondition(CScript const& scriptSig)
|
||||
{
|
||||
auto pc = scriptSig.begin();
|
||||
@@ -709,6 +636,57 @@ CPubKey check_signing_pubkey(CScript scriptSig)
|
||||
return CPubKey();
|
||||
}
|
||||
|
||||
|
||||
// returns total of normal inputs signed with this pubkey
|
||||
int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey)
|
||||
{
|
||||
int64_t total = 0;
|
||||
for (auto vin : tx.vin) {
|
||||
CTransaction vintx;
|
||||
uint256 hashBlock;
|
||||
if (!IsCCInput(vin.scriptSig) && myGetTransaction(vin.prevout.hash, vintx, hashBlock)) {
|
||||
typedef std::vector<unsigned char> valtype;
|
||||
std::vector<valtype> vSolutions;
|
||||
txnouttype whichType;
|
||||
|
||||
if (Solver(vintx.vout[vin.prevout.n].scriptPubKey, whichType, vSolutions)) {
|
||||
switch (whichType) {
|
||||
case TX_PUBKEY:
|
||||
if (pubkey == CPubKey(vSolutions[0])) // is my input?
|
||||
total += vintx.vout[vin.prevout.n].nValue;
|
||||
break;
|
||||
case TX_PUBKEYHASH:
|
||||
if (pubkey.GetID() == CKeyID(uint160(vSolutions[0]))) // is my input?
|
||||
total += vintx.vout[vin.prevout.n].nValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
// returns total of CC inputs signed with this pubkey
|
||||
int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey)
|
||||
{
|
||||
int64_t total = 0;
|
||||
for (auto vin : tx.vin) {
|
||||
if (IsCCInput(vin.scriptSig)) {
|
||||
CPubKey vinPubkey = check_signing_pubkey(vin.scriptSig);
|
||||
if (vinPubkey.IsValid()) {
|
||||
if (vinPubkey == pubkey) {
|
||||
CTransaction vintx;
|
||||
uint256 hashBlock;
|
||||
if (myGetTransaction(vin.prevout.hash, vintx, hashBlock)) {
|
||||
total += vintx.vout[vin.prevout.n].nValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
bool ProcessCC(struct CCcontract_info *cp,Eval* eval, std::vector<uint8_t> paramsNull,const CTransaction &ctx, unsigned int nIn)
|
||||
{
|
||||
CTransaction createTx; uint256 assetid,assetid2,hashBlock; uint8_t funcid; int32_t height,i,n,from_mempool = 0; int64_t amount; std::vector<uint8_t> origpubkey;
|
||||
|
||||
@@ -664,6 +664,7 @@ int32_t z_exportkey(char *privkey,char *refcoin,char *acname,char *zaddr)
|
||||
free(retstr);
|
||||
return(0);
|
||||
}
|
||||
return(-1);
|
||||
}
|
||||
|
||||
int32_t getnewaddress(char *coinaddr,char *refcoin,char *acname)
|
||||
|
||||
@@ -571,8 +571,6 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid("invalid gatewaysbind OP_RETURN data!");
|
||||
else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for gatewaysbind!");
|
||||
else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 )
|
||||
return eval->Invalid("vin.1 is CC for gatewaysbind!");
|
||||
else if ( ConstrainVout(tmptx.vout[0],1,gatewaystokensaddr,totalsupply)==0)
|
||||
return eval->Invalid("invalid tokens to gateways vout for gatewaysbind!");
|
||||
else if ( ConstrainVout(tmptx.vout[1],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0)
|
||||
@@ -599,6 +597,8 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid(validationError);
|
||||
}
|
||||
else if ( DecodeOraclesCreateOpRet(tmptx.vout[numvouts-1].scriptPubKey,name,description,format) != 'C' )
|
||||
return eval->Invalid("invalid oraclescreate OP_RETURN data");
|
||||
else if (refcoin!=name)
|
||||
{
|
||||
sprintf(validationError,"mismatched oracle name %s != %s\n",name.c_str(),refcoin.c_str());
|
||||
return eval->Invalid(validationError);
|
||||
@@ -632,10 +632,8 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid("invalid gatewaysdeposittxid!");
|
||||
else if (IsCCInput(tx.vin[0].scriptSig) != 0)
|
||||
return eval->Invalid("vin.0 is normal for gatewaysclaim!");
|
||||
else if (IsCCInput(tx.vin[1].scriptSig) == 0)
|
||||
return eval->Invalid("vin.1 is CC for gatewaysclaim!");
|
||||
else if ((*cp->ismyvin)(tx.vin[2].scriptSig) == 0 || myGetTransaction(tx.vin[2].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[2].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin.2 is CC marker for gatewaysclaim or invalid marker amount!");
|
||||
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewaysclaim or invalid marker amount!");
|
||||
else if (_GetCCaddress(destaddr,EVAL_TOKENS,pubkey)==0 || ConstrainVout(tx.vout[0],1,destaddr,amount)==0)
|
||||
return eval->Invalid("invalid vout tokens to destpub for gatewaysclaim!");
|
||||
else if (numvouts>2 && (myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || ConstrainVout(tx.vout[1],1,gatewaystokensaddr,tmptx.vout[tx.vin[1].prevout.n].nValue-amount)==0))
|
||||
@@ -695,8 +693,6 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid("refcoin different than in bind tx");
|
||||
else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for gatewaysWithdraw!");
|
||||
else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 )
|
||||
return eval->Invalid("vin.1 is CC for gatewaysWithdraw!");
|
||||
else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0)
|
||||
return eval->Invalid("invalid marker vout for gatewaysWithdraw!");
|
||||
else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0)
|
||||
@@ -717,8 +713,8 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!");
|
||||
else if (IsCCInput(tx.vin[0].scriptSig) != 0)
|
||||
return eval->Invalid("vin.0 is normal for gatewayspartialsign!");
|
||||
else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin.1 is CC marker for gatewayspartialsign or invalid marker amount!");
|
||||
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewayspartialsign or invalid marker amount!");
|
||||
else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 )
|
||||
return eval->Invalid("vout.0 invalid marker for gatewayspartialsign!");
|
||||
else if (K>M)
|
||||
@@ -739,8 +735,6 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid("refcoin different than in bind tx");
|
||||
else if ( IsCCInput(tmptx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for gatewaysWithdraw!");
|
||||
else if ( IsCCInput(tmptx.vin[1].scriptSig) == 0 )
|
||||
return eval->Invalid("vin.1 is CC for gatewaysWithdraw!");
|
||||
else if ( ConstrainVout(tmptx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE)==0)
|
||||
return eval->Invalid("invalid marker vout for gatewaysWithdraw!");
|
||||
else if ( ConstrainVout(tmptx.vout[1],1,gatewaystokensaddr,amount)==0)
|
||||
@@ -761,8 +755,8 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!");
|
||||
else if (IsCCInput(tx.vin[0].scriptSig) != 0)
|
||||
return eval->Invalid("vin.0 is normal for gatewayscompletesigning!");
|
||||
else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin.1 is CC marker for gatewayscompletesigning or invalid marker amount!");
|
||||
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewayscompletesigning or invalid marker amount!");
|
||||
else if (ConstrainVout(tx.vout[0],1,cp->unspendableCCaddr,CC_MARKER_VALUE) == 0 )
|
||||
return eval->Invalid("vout.0 invalid marker for gatewayscompletesigning!");
|
||||
else if (K<M)
|
||||
@@ -800,8 +794,8 @@ bool GatewaysValidate(struct CCcontract_info *cp,Eval *eval,const CTransaction &
|
||||
return eval->Invalid("gatewaysbind tx is not yet confirmed(notarised)!");
|
||||
else if ( IsCCInput(tx.vin[0].scriptSig) != 0 )
|
||||
return eval->Invalid("vin.0 is normal for gatewaysmarkdone!");
|
||||
else if ((*cp->ismyvin)(tx.vin[1].scriptSig) == 0 || myGetTransaction(tx.vin[1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin.1 is CC marker for gatewaysmarkdone or invalid marker amount!");
|
||||
else if ((*cp->ismyvin)(tx.vin[tx.vin.size()-1].scriptSig) == 0 || myGetTransaction(tx.vin[tx.vin.size()-1].prevout.hash,tmptx,hashblock)==0 || tmptx.vout[tx.vin[tx.vin.size()-1].prevout.n].nValue!=CC_MARKER_VALUE)
|
||||
return eval->Invalid("vin."+std::to_string(tx.vin.size()-1)+" is CC marker for gatewaysmarkdone or invalid marker amount!");
|
||||
else if (K<M)
|
||||
return eval->Invalid("invalid number of signs!");
|
||||
break;
|
||||
|
||||
@@ -20,47 +20,35 @@
|
||||
#include "primitives/transaction.h"
|
||||
#include "cc/CCinclude.h"
|
||||
#include <openssl/sha.h>
|
||||
#include "cc/CCtokens.h"
|
||||
|
||||
#include "key_io.h"
|
||||
#define CODA_BURN_ADDRESS "KPrrRoPfHOnNpZZQ6laHXdQDkSQDkVHaN0V+LizLlHxz7NaA59sBAAAA"
|
||||
/*
|
||||
* CC Eval method for import coin.
|
||||
*
|
||||
* This method should control every parameter of the ImportCoin transaction, since it has no signature
|
||||
* to protect it from malleability.
|
||||
|
||||
##### 0xffffffff is a special CCid for single chain/dual daemon imports
|
||||
*/
|
||||
|
||||
extern std::string ASSETCHAINS_SELFIMPORT;
|
||||
extern uint16_t ASSETCHAINS_CODAPORT,ASSETCHAINS_BEAMPORT;
|
||||
extern uint8_t ASSETCHAINS_OVERRIDE_PUBKEY33[33];
|
||||
extern uint256 KOMODO_EARLYTXID;
|
||||
|
||||
// utilities from gateways.cpp
|
||||
uint256 BitcoinGetProofMerkleRoot(const std::vector<uint8_t> &proofData, std::vector<uint256> &txids);
|
||||
uint256 GatewaysReverseScan(uint256 &txid, int32_t height, uint256 reforacletxid, uint256 batontxid);
|
||||
int32_t GatewaysCointxidExists(struct CCcontract_info *cp, uint256 cointxid);
|
||||
uint8_t DecodeGatewaysBindOpRet(char *depositaddr,const CScript &scriptPubKey,uint256 &tokenid,std::string &coin,int64_t &totalsupply,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector<CPubKey> &gatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype);
|
||||
uint8_t DecodeImportGatewayBindOpRet(char *burnaddr,const CScript &scriptPubKey,std::string &coin,uint256 &oracletxid,uint8_t &M,uint8_t &N,std::vector<CPubKey> &importgatewaypubkeys,uint8_t &taddr,uint8_t &prefix,uint8_t &prefix2,uint8_t &wiftype);
|
||||
int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvout,std::string refcoin,uint256 burntxid,const std::string deposithex,std::vector<uint8_t>proof,uint256 merkleroot,CPubKey destpub,uint8_t taddr,uint8_t prefix,uint8_t prefix2);
|
||||
char *nonportable_path(char *str);
|
||||
char *portable_path(char *str);
|
||||
void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep);
|
||||
void *filestr(long *allocsizep,char *_fname);
|
||||
|
||||
// ac_import=chain support:
|
||||
// encode opret for gateways import
|
||||
CScript EncodeImportTxOpRet(uint32_t targetCCid, std::string coin, std::vector<CPubKey> publishers, std::vector<uint256>txids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vector<uint8_t>proof, CPubKey destpub, int64_t amount)
|
||||
{
|
||||
CScript opret;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << targetCCid << coin << publishers << txids << height << cointxid << claimvout << rawburntx << proof << destpub << amount);
|
||||
return(opret);
|
||||
}
|
||||
|
||||
CScript EncodeGatewaysImportTxOpRet(uint32_t targetCCid, std::string coin, uint256 bindtxid, std::vector<CPubKey> publishers, std::vector<uint256>txids, int32_t height, uint256 cointxid, int32_t claimvout, std::string rawburntx, std::vector<uint8_t>proof, CPubKey destpub, int64_t amount)
|
||||
{
|
||||
CScript opret;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << targetCCid << coin << bindtxid << publishers << txids << height << cointxid << claimvout << rawburntx << proof << destpub << amount);
|
||||
return(opret);
|
||||
}
|
||||
|
||||
CScript EncodeCodaImportTxOpRet(uint32_t targetCCid, std::string coin, std::string burntx, uint256 bindtxid, CPubKey destpub, int64_t amount)
|
||||
{
|
||||
CScript opret;
|
||||
opret << OP_RETURN << E_MARSHAL(ss << targetCCid << burntx << bindtxid << destpub << amount);
|
||||
return(opret);
|
||||
}
|
||||
|
||||
cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,char const *arg3,char const *arg4,char const *arg5)
|
||||
{
|
||||
char cmdstr[5000],fname[256],*jsonstr;
|
||||
@@ -81,165 +69,66 @@ cJSON* CodaRPC(char **retstr,char const *arg0,char const *arg1,char const *arg2,
|
||||
return(retjson);
|
||||
}
|
||||
|
||||
bool ImportCoinGatewaysVerify(CTransaction oracletx, int32_t claimvout, std::string refcoin, uint256 burntxid, const std::string rawburntx, std::vector<uint8_t>proof, uint256 merkleroot)
|
||||
{
|
||||
std::vector<uint256> txids;
|
||||
uint256 proofroot;
|
||||
std::string name, description, format;
|
||||
int32_t i, numvouts;
|
||||
|
||||
if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey, name, description, format) != 'C' || name != refcoin)
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "ImportCoinGatewaysVerify mismatched oracle name=" << name.c_str() << " != " << refcoin.c_str() << std::endl);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "verified proof for burntxid=" << burntxid.GetHex() << " in trusted merkleroot" << std::endl);
|
||||
return true;
|
||||
}
|
||||
|
||||
// make import tx with burntx and its proof of existence
|
||||
// std::string MakeGatewaysImportTx(uint64_t txfee, uint256 oracletxid, int32_t height, std::string refcoin, std::vector<uint8_t> proof, std::string rawburntx, int32_t ivout, uint256 burntxid,std::string destaddr)
|
||||
// {
|
||||
// CMutableTransaction burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
// CTransaction oracletx,regtx; CPubKey regpk;
|
||||
// uint256 proofroot,txid,tmporacletxid,merkleroot,mhash,hashBlock; int32_t i,m,n=0,numvouts;
|
||||
// std::string name,desc,format; std::vector<CTxOut> vouts;
|
||||
// std::vector<CPubKey> pubkeys; std::vector<uint256>txids;
|
||||
// char markeraddr[64]; int64_t datafee;
|
||||
// std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
|
||||
// if (!E_UNMARSHAL(ParseHex(rawburntx), ss >> burntx))
|
||||
// return std::string("");
|
||||
// CAmount amount = GetCoinImportValue(burntx); // equal to int64_t
|
||||
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx height=" << height << " coin=" << refcoin << " amount=" << (double)amount / COIN << " pubkeys num=" << pubkeys.size() << std::endl);
|
||||
// if (GetTransaction(oracletxid, oracletx, hashBlock, false) == 0 || (numvouts = oracletx.vout.size()) <= 0)
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx cant find oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
// return("");
|
||||
// }
|
||||
// if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C')
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
// return("");
|
||||
// }
|
||||
// if (name!=refcoin || format!="Ihh")
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl);
|
||||
// return("");
|
||||
// }
|
||||
// CCtxidaddr(markeraddr,oracletxid);
|
||||
// SetCCunspents(unspentOutputs,markeraddr,true);
|
||||
// for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
// {
|
||||
// txid = it->first.txhash;
|
||||
// if ( GetTransaction(txid,regtx,hashBlock,false) != 0 && regtx.vout.size() > 0
|
||||
// && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid )
|
||||
// {
|
||||
// pubkeys.push_back(regpk);
|
||||
// n++;
|
||||
// }
|
||||
// }
|
||||
// merkleroot = zeroid;
|
||||
// for (i = m = 0; i < n; i++)
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx using pubkeys[" << i << "]=" << HexStr(pubkeys[i]) << std::endl);
|
||||
// if ((mhash = CCOraclesReverseScan("importcoind-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid)
|
||||
// {
|
||||
// if (merkleroot == zeroid)
|
||||
// merkleroot = mhash, m = 1;
|
||||
// else if (mhash == merkleroot)
|
||||
// m ++;
|
||||
// txids.push_back(txid);
|
||||
// }
|
||||
// }
|
||||
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "MakeGatewaysImportTx burntxid=" << burntxid.GetHex() << " nodes m=" << m << " of n=" << n << std::endl);
|
||||
// if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl );
|
||||
// return("");
|
||||
// }
|
||||
// proofroot = BitcoinGetProofMerkleRoot(proof, txids);
|
||||
// if (proofroot != merkleroot)
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx mismatched proof merkleroot=" << proofroot.GetHex() << " and oracles merkleroot=" << merkleroot.GetHex() << std::endl);
|
||||
// return("");
|
||||
// }
|
||||
// // check the burntxid is in the proof:
|
||||
// if (std::find(txids.begin(), txids.end(), burntxid) == txids.end()) {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeGatewaysImportTx invalid proof for this burntxid=" << burntxid.GetHex() << std::endl);
|
||||
// return("");
|
||||
// }
|
||||
// burntx.vout.push_back(MakeBurnOutput(amount,0xffffffff,refcoin,vouts,proof,oracletxid,height));
|
||||
// std::vector<uint256> leaftxids;
|
||||
// BitcoinGetProofMerkleRoot(proof, leaftxids);
|
||||
// MerkleBranch newBranch(0, leaftxids);
|
||||
// TxProof txProof = std::make_pair(burntxid, newBranch);
|
||||
// CTxDestination dest = DecodeDestination(destaddr.c_str());
|
||||
// CScript scriptPubKey = GetScriptForDestination(dest);
|
||||
// vouts.push_back(CTxOut(amount,scriptPubKey));
|
||||
// return HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(txProof, burntx, vouts)));
|
||||
// }
|
||||
|
||||
// makes source tx for self import tx
|
||||
std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx)
|
||||
CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount)
|
||||
{
|
||||
const int64_t txfee = 10000;
|
||||
int64_t inputs, change;
|
||||
CPubKey myPubKey = Mypubkey();
|
||||
struct CCcontract_info *cpDummy, C;
|
||||
|
||||
cpDummy = CCinit(&C, EVAL_TOKENS);
|
||||
cpDummy = CCinit(&C, EVAL_TOKENS); // this is just for FinalizeCCTx to work
|
||||
|
||||
mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
|
||||
if( (inputs = AddNormalinputs(mtx, myPubKey, txfee, 4)) == 0 ) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx: cannot find normal imputs for txfee" << std::endl);
|
||||
return std::string("");
|
||||
if (AddNormalinputs(mtx, myPubKey, 2 * txfee, 4) == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeSelfImportSourceTx() warning: cannot find normal inputs for txfee" << std::endl);
|
||||
}
|
||||
|
||||
|
||||
CScript scriptPubKey = GetScriptForDestination(dest);
|
||||
mtx.vout.push_back(CTxOut(txfee, scriptPubKey));
|
||||
change = inputs - txfee;
|
||||
if( change != 0 )
|
||||
mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG));
|
||||
|
||||
//make opret with amount:
|
||||
return FinalizeCCTx(0, cpDummy, mtx, myPubKey, txfee, CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN << (uint8_t)'A' << amount));
|
||||
//make opret with 'burned' amount:
|
||||
FinalizeCCTx(0, cpDummy, mtx, myPubKey, txfee, CScript() << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN << (uint8_t)'A' << amount));
|
||||
return mtx;
|
||||
}
|
||||
|
||||
// make sure vin0 is signed by ASSETCHAINS_OVERRIDE_PUBKEY33
|
||||
int32_t CheckVin0PubKey(const CTransaction &sourcetx)
|
||||
// make sure vin is signed by pubkey33
|
||||
bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33])
|
||||
{
|
||||
CTransaction vintx;
|
||||
uint256 blockHash;
|
||||
char destaddr[64], pkaddr[64];
|
||||
|
||||
if( !myGetTransaction(sourcetx.vin[0].prevout.hash, vintx, blockHash) ) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVin0PubKey() could not load vintx" << sourcetx.vin[0].prevout.hash.GetHex() << std::endl);
|
||||
return(-1);
|
||||
if (i < 0 || i >= sourcetx.vin.size())
|
||||
return false;
|
||||
|
||||
if( !myGetTransaction(sourcetx.vin[i].prevout.hash, vintx, blockHash) ) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() could not load vintx" << sourcetx.vin[i].prevout.hash.GetHex() << std::endl);
|
||||
return false;
|
||||
}
|
||||
if( sourcetx.vin[0].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[0].prevout.n].scriptPubKey) != 0 )
|
||||
if( sourcetx.vin[i].prevout.n < vintx.vout.size() && Getscriptaddress(destaddr, vintx.vout[sourcetx.vin[i].prevout.n].scriptPubKey) != 0 )
|
||||
{
|
||||
pubkey2addr(pkaddr, ASSETCHAINS_OVERRIDE_PUBKEY33);
|
||||
pubkey2addr(pkaddr, pubkey33);
|
||||
if (strcmp(pkaddr, destaddr) == 0) {
|
||||
return(0);
|
||||
return true;
|
||||
}
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVin0PubKey() mismatched vin0[prevout.n=" << sourcetx.vin[0].prevout.n << "] -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl);
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckVinPubKey() mismatched vin[" << i << "].prevout.n=" << sourcetx.vin[i].prevout.n << " -> destaddr=" << destaddr << " vs pkaddr=" << pkaddr << std::endl);
|
||||
}
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
// ac_import=PUBKEY support:
|
||||
// prepare a tx for creating import tx and quasi-burn tx
|
||||
int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount) // find burnTx with hash from "other" daemon
|
||||
int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransaction &templateMtx, ImportProof &proofNull) // find burnTx with hash from "other" daemon
|
||||
{
|
||||
MerkleBranch newBranch;
|
||||
CMutableTransaction tmpmtx;
|
||||
CTransaction sourcetx;
|
||||
//CTransaction sourcetx;
|
||||
|
||||
tmpmtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
|
||||
/*
|
||||
if (!E_UNMARSHAL(ParseHex(rawsourcetx), ss >> sourcetx)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: could not unmarshal source tx" << std::endl);
|
||||
return(-1);
|
||||
@@ -248,9 +137,9 @@ int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript
|
||||
if (sourcetx.vout.size() == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: vout size is 0" << std::endl);
|
||||
return -1;
|
||||
}
|
||||
} */
|
||||
|
||||
if (ivout < 0) { // "ivout < 0" means "find"
|
||||
/*if (ivout < 0) { // "ivout < 0" means "find"
|
||||
// try to find vout
|
||||
CPubKey myPubkey = Mypubkey();
|
||||
ivout = 0;
|
||||
@@ -262,38 +151,49 @@ int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript
|
||||
if (ivout >= sourcetx.vout.size()) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: needed vout not found" << std::endl);
|
||||
return -1;
|
||||
}
|
||||
} */
|
||||
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
|
||||
int32_t ivout = 0;
|
||||
|
||||
scriptPubKey = sourcetx.vout[ivout].scriptPubKey;
|
||||
// LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "GetSelfimportProof: using vout[" << ivout << "] of the passed rawtx" << std::endl);
|
||||
|
||||
CScript scriptPubKey = sourceMtx.vout[ivout].scriptPubKey;
|
||||
|
||||
//mtx is template for import tx
|
||||
mtx = sourcetx;
|
||||
mtx.fOverwintered = tmpmtx.fOverwintered;
|
||||
templateMtx = sourceMtx;
|
||||
templateMtx.fOverwintered = tmpmtx.fOverwintered;
|
||||
|
||||
//malleability fix for burn tx:
|
||||
//mtx.nExpiryHeight = tmpmtx.nExpiryHeight;
|
||||
mtx.nExpiryHeight = sourcetx.nExpiryHeight;
|
||||
templateMtx.nExpiryHeight = sourceMtx.nExpiryHeight;
|
||||
|
||||
mtx.nVersionGroupId = tmpmtx.nVersionGroupId;
|
||||
mtx.nVersion = tmpmtx.nVersion;
|
||||
mtx.vout.clear();
|
||||
mtx.vout.resize(1);
|
||||
mtx.vout[0].nValue = burnAmount;
|
||||
mtx.vout[0].scriptPubKey = scriptPubKey;
|
||||
templateMtx.nVersionGroupId = tmpmtx.nVersionGroupId;
|
||||
templateMtx.nVersion = tmpmtx.nVersion;
|
||||
templateMtx.vout.clear();
|
||||
templateMtx.vout.resize(1);
|
||||
|
||||
// not sure we need this now as we create sourcetx ourselves:
|
||||
if (sourcetx.GetHash() != sourcetxid) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
// check ac_pubkey:
|
||||
if (CheckVin0PubKey(sourcetx) < 0) {
|
||||
uint8_t evalCode, funcId;
|
||||
int64_t burnAmount;
|
||||
vscript_t vopret;
|
||||
if( !GetOpReturnData(sourceMtx.vout.back().scriptPubKey, vopret) ||
|
||||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> burnAmount)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof() could not unmarshal source tx opret" << std::endl);
|
||||
return -1;
|
||||
}
|
||||
proof = std::make_pair(sourcetxid, newBranch);
|
||||
templateMtx.vout[0].nValue = burnAmount;
|
||||
templateMtx.vout[0].scriptPubKey = scriptPubKey;
|
||||
|
||||
// not sure we need this now as we create sourcetx ourselves:
|
||||
/*if (sourcetx.GetHash() != sourcetxid) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetSelfimportProof: passed source txid incorrect" << std::endl);
|
||||
return(-1);
|
||||
}*/
|
||||
|
||||
// check ac_pubkey:
|
||||
if (!CheckVinPubKey(sourceMtx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
|
||||
return -1;
|
||||
}
|
||||
proofNull = ImportProof(std::make_pair(sourceMtx.GetHash(), newBranch));
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -452,71 +352,123 @@ int32_t CheckCODAimport(CTransaction importTx,CTransaction burnTx,std::vector<CT
|
||||
}
|
||||
|
||||
int32_t CheckGATEWAYimport(CTransaction importTx,CTransaction burnTx,std::string refcoin,std::vector<uint8_t> proof,
|
||||
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256> txids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub)
|
||||
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256> txids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount)
|
||||
{
|
||||
// CTransaction oracletx,regtx; CPubKey regpk;
|
||||
// uint256 proofroot,txid,tmporacletxid,merkleroot,mhash,hashBlock; int32_t i,m,n=0,numvouts;
|
||||
// std::string name,desc,format; std::vector<CTxOut> vouts;
|
||||
// std::vector<CPubKey> pubkeys; std::vector<uint256>txids;
|
||||
// char markeraddr[64]; int64_t datafee;
|
||||
// std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
CTransaction oracletx,bindtx,regtx; int32_t i,m,n=0,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype;
|
||||
uint256 txid,oracletxid,tmporacletxid,merkleroot,mhash,hashBlock;
|
||||
std::string name,desc,format,coin; std::vector<CTxOut> vouts; CPubKey regpk;
|
||||
std::vector<CPubKey> pubkeys,tmppubkeys,tmppublishers; char markeraddr[64],deposit[64],destaddr[64],tmpdest[64]; int64_t datafee;
|
||||
std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> > unspentOutputs;
|
||||
|
||||
// ASSETCHAINS_SELFIMPORT is coin
|
||||
if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid import gateway. On this chain only valid import gateway is " << KOMODO_EARLYTXID.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
// check for valid burn from external coin blockchain and if valid return(0);
|
||||
// if (GetTransaction(oracletxid, oracletx, hashBlock, false) == 0 || (numvouts = oracletx.vout.size()) <= 0)
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
// return(-1);
|
||||
// }
|
||||
// if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C')
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
// return(-1);
|
||||
// }
|
||||
// if (name!=refcoin || format!="Ihh")
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl);
|
||||
// return(-1);
|
||||
// }
|
||||
// CCtxidaddr(markeraddr,oracletxid);
|
||||
// SetCCunspents(unspentOutputs,markeraddr,true);
|
||||
// for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
// {
|
||||
// txid = it->first.txhash;
|
||||
// if ( GetTransaction(txid,regtx,hashBlock,false) != 0 && regtx.vout.size() > 0
|
||||
// && DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid )
|
||||
// {
|
||||
// pubkeys.push_back(regpk);
|
||||
// n++;
|
||||
// }
|
||||
// }
|
||||
// merkleroot = zeroid;
|
||||
// for (i = m = 0; i < n; i++)
|
||||
// {
|
||||
// if ((mhash = CCOraclesReverseScan("importcoind-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid)
|
||||
// {
|
||||
// if (merkleroot == zeroid)
|
||||
// merkleroot = mhash, m = 1;
|
||||
// else if (mhash == merkleroot)
|
||||
// m ++;
|
||||
// txids.push_back(txid);
|
||||
// }
|
||||
// }
|
||||
// if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl );
|
||||
// return(-1);
|
||||
// }
|
||||
// proofroot = BitcoinGetProofMerkleRoot(proof, txids);
|
||||
// if (proofroot != merkleroot)
|
||||
// {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport mismatched proof merkleroot=" << proofroot.GetHex() << " and oracles merkleroot=" << merkleroot.GetHex() << std::endl);
|
||||
// return(-1);
|
||||
// }
|
||||
// // check the burntxid is in the proof:
|
||||
// if (std::find(txids.begin(), txids.end(), burnTx.GetHash()) == txids.end()) {
|
||||
// LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid proof for this burntxid=" << burnTx.GetHash().GetHex() << std::endl);
|
||||
// return(-1);
|
||||
// }
|
||||
if (GetTransaction(bindtxid, bindtx, hashBlock, false) == 0 || (numvouts = bindtx.vout.size()) <= 0)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find bindtxid=" << bindtxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (DecodeImportGatewayBindOpRet(deposit,bindtx.vout[numvouts - 1].scriptPubKey,coin,oracletxid,M,N,tmppubkeys,taddr,prefix,prefix2,wiftype) != 'B')
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckGATEWAYimport invalid bind tx. bindtxid=" << bindtxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (refcoin!=coin)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid coin " << refcoin << "!=" << coin << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if ( N == 0 || N > 15 || M > N )
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid N or M " << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (tmppubkeys.size()!=N)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport not enough pubkeys for given N " << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (komodo_txnotarizedconfirmed(bindtxid) == false)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport bindtx not yet confirmed/notarized" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (GetTransaction(oracletxid, oracletx, hashBlock, false) == 0 || (numvouts = oracletx.vout.size()) <= 0)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport cant find oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (DecodeOraclesCreateOpRet(oracletx.vout[numvouts - 1].scriptPubKey,name,desc,format) != 'C')
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle tx. oracletxid=" << oracletxid.GetHex() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (name!=refcoin || format!="Ihh")
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport invalid oracle name or format tx. oracletxid=" << oracletxid.GetHex() << " name=" << name << " format=" << format << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
CCtxidaddr(markeraddr,oracletxid);
|
||||
SetCCunspents(unspentOutputs,markeraddr,false);
|
||||
for (std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue> >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
if ( GetTransaction(txid,regtx,hashBlock,false) != 0 && regtx.vout.size() > 0
|
||||
&& DecodeOraclesOpRet(regtx.vout[regtx.vout.size()-1].scriptPubKey,tmporacletxid,regpk,datafee) == 'R' && oracletxid == tmporacletxid )
|
||||
{
|
||||
pubkeys.push_back(regpk);
|
||||
n++;
|
||||
}
|
||||
}
|
||||
if (pubkeys.size()!=tmppubkeys.size())
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of bind and oracle pubkeys " << tmppubkeys.size() << "!=" << pubkeys.size() << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
merkleroot = zeroid;
|
||||
for (i = m = 0; i < n; i++)
|
||||
{
|
||||
if ((mhash = CCOraclesReverseScan("importgateway-1",txid, height, oracletxid, OraclesBatontxid(oracletxid, pubkeys[i]))) != zeroid)
|
||||
{
|
||||
if (merkleroot == zeroid)
|
||||
merkleroot = mhash, m = 1;
|
||||
else if (mhash == merkleroot)
|
||||
m ++;
|
||||
tmppublishers.push_back(pubkeys[i]);
|
||||
}
|
||||
}
|
||||
if (publishers.size()!=tmppublishers.size())
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport different number of publishers for burtx in oracle" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (merkleroot == zeroid || m < n / 2) // none or less than half oracle nodes sent merkleroot
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport couldnt find merkleroot for block height=" << height << "coin=" << refcoin.c_str() << " oracleid=" << oracletxid.GetHex() << " m=" << m << " vs n=" << n << std::endl );
|
||||
return(-1);
|
||||
}
|
||||
else if ( ImportGatewayVerify(deposit,oracletxid,burnvout,refcoin,burntxid,rawburntx,proof,merkleroot,destpub,taddr,prefix,prefix2) != amount )
|
||||
{
|
||||
CCerror = strprintf("burntxid didnt validate !");
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
else if (importTx.vout[0].nValue!=amount)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import amount different than in burntx" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
Getscriptaddress(destaddr,importTx.vout[0].scriptPubKey);
|
||||
Getscriptaddress(tmpdest,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG);
|
||||
if (strcmp(destaddr,tmpdest)!=0)
|
||||
{
|
||||
LOGSTREAM("importgateway", CCLOG_INFO, stream << "CheckGATEWAYimport import coins destination different than in burntx" << std::endl);
|
||||
return(-1);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
@@ -545,7 +497,7 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransacti
|
||||
}
|
||||
|
||||
//ac_pubkey check:
|
||||
if (CheckVin0PubKey(sourcetx) < 0) {
|
||||
if (!CheckVinPubKey(sourcetx, 0, ASSETCHAINS_OVERRIDE_PUBKEY33)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -554,9 +506,11 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransacti
|
||||
uint8_t evalCode, funcId;
|
||||
int64_t amount;
|
||||
|
||||
GetOpReturnData(sourcetx.vout.back().scriptPubKey, vopret);
|
||||
if (vopret.size() == 0 || !E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) || evalCode != EVAL_IMPORTCOIN || funcId != 'A') {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "no or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
if (!GetOpReturnData(sourcetx.vout.back().scriptPubKey, vopret) ||
|
||||
vopret.size() == 0 ||
|
||||
!E_UNMARSHAL(vopret, ss >> evalCode; ss >> funcId; ss >> amount) ||
|
||||
evalCode != EVAL_IMPORTCOIN || funcId != 'A') {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "none or incorrect opret to validate in source txid=" << sourcetxid.GetHex() << std::endl);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -571,21 +525,154 @@ int32_t CheckPUBKEYimport(TxProof proof,std::vector<uint8_t> rawproof,CTransacti
|
||||
return(0);
|
||||
}
|
||||
|
||||
/*
|
||||
* CC Eval method for import coin.
|
||||
*
|
||||
* This method should control every parameter of the ImportCoin transaction, since it has no signature
|
||||
* to protect it from malleability.
|
||||
|
||||
##### 0xffffffff is a special CCid for single chain/dual daemon imports
|
||||
*/
|
||||
bool Eval::ImportCoin(const std::vector<uint8_t> params,const CTransaction &importTx,unsigned int nIn)
|
||||
bool CheckMigration(Eval *eval, const CTransaction &importTx, const CTransaction &burnTx, std::vector<CTxOut> & payouts, const ImportProof &proof, const std::vector<uint8_t> &rawproof)
|
||||
{
|
||||
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; uint64_t txfee = 10000; int32_t height,burnvout; std::vector<CPubKey> publishers;
|
||||
uint32_t targetCcid; std::string targetSymbol,srcaddr,destaddr,receipt,rawburntx; uint256 payoutsHash,bindtxid; std::vector<uint8_t> rawproof;
|
||||
std::vector<uint256> txids; CPubKey destpub;
|
||||
vscript_t vimportOpret;
|
||||
if (!GetOpReturnData(importTx.vout.back().scriptPubKey, vimportOpret) ||
|
||||
vimportOpret.empty())
|
||||
return eval->Invalid("invalid-import-tx-no-opret");
|
||||
|
||||
if ( importTx.vout.size() < 2 )
|
||||
|
||||
uint256 tokenid = zeroid;
|
||||
if (vimportOpret.begin()[0] == EVAL_TOKENS) { // for tokens (new opret with tokens)
|
||||
struct CCcontract_info *cpTokens, CCtokens_info;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
vscript_t vnonfungibleOpret;
|
||||
|
||||
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
|
||||
|
||||
if (DecodeTokenOpRet(importTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0)
|
||||
return eval->Invalid("cannot-decode-import-tx-token-opret");
|
||||
|
||||
uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init to no non-fungibles
|
||||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret);
|
||||
if (!vnonfungibleOpret.empty())
|
||||
nonfungibleEvalCode = vnonfungibleOpret.begin()[0];
|
||||
|
||||
// check if burn tx at least has cc evaltoken vins (we cannot get cc input)
|
||||
bool hasTokenVin = false;
|
||||
for (auto vin : burnTx.vin)
|
||||
if (cpTokens->ismyvin(vin.scriptSig))
|
||||
hasTokenVin = true;
|
||||
if (!hasTokenVin)
|
||||
return eval->Invalid("burn-tx-has-no-token-vins");
|
||||
|
||||
// calc outputs for burn tx
|
||||
CAmount ccBurnOutputs = 0;
|
||||
for (auto v : burnTx.vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey
|
||||
ccBurnOutputs += v.nValue;
|
||||
|
||||
// calc outputs for import tx
|
||||
CAmount ccImportOutputs = 0;
|
||||
for (auto v : importTx.vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
!IsTokenMarkerVout(v)) // should not be marker here
|
||||
ccImportOutputs += v.nValue;
|
||||
|
||||
if (ccBurnOutputs != ccImportOutputs)
|
||||
return eval->Invalid("token-cc-burned-output-not-equal-cc-imported-output");
|
||||
|
||||
}
|
||||
else if (vimportOpret.begin()[0] != EVAL_IMPORTCOIN) {
|
||||
return eval->Invalid("import-tx-incorrect-opret-eval");
|
||||
}
|
||||
|
||||
// for tokens check burn, import, tokenbase tx
|
||||
if (!tokenid.IsNull()) {
|
||||
|
||||
std::string sourceSymbol;
|
||||
CTransaction tokenbaseTx;
|
||||
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol; ss >> tokenbaseTx))
|
||||
return eval->Invalid("cannot-unmarshal-rawproof-for-tokens");
|
||||
|
||||
uint256 sourceTokenId;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
if (burnTx.vout.size() > 0 && DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, sourceTokenId, voutTokenPubkeys, oprets) == 0)
|
||||
return eval->Invalid("cannot-decode-burn-tx-token-opret");
|
||||
|
||||
if (sourceTokenId != tokenbaseTx.GetHash()) // check tokenid in burn tx opret maches the passed tokenbase tx (to prevent cheating by importing user)
|
||||
return eval->Invalid("incorrect-token-creation-tx-passed");
|
||||
|
||||
std::vector<std::pair<uint8_t, vscript_t>> opretsSrc;
|
||||
vscript_t vorigpubkeySrc;
|
||||
std::string nameSrc, descSrc;
|
||||
if (DecodeTokenCreateOpRet(tokenbaseTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsSrc) == 0)
|
||||
return eval->Invalid("cannot-decode-token-creation-tx");
|
||||
|
||||
std::vector<std::pair<uint8_t, vscript_t>> opretsImport;
|
||||
vscript_t vorigpubkeyImport;
|
||||
std::string nameImport, descImport;
|
||||
if (importTx.vout.size() == 0 || DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkeySrc, nameSrc, descSrc, opretsImport) == 0)
|
||||
return eval->Invalid("cannot-decode-token-import-tx");
|
||||
|
||||
// check that name,pubkey,description in import tx correspond ones in token creation tx in the source chain:
|
||||
if (vorigpubkeySrc != vorigpubkeyImport ||
|
||||
nameSrc != nameImport ||
|
||||
descSrc != descImport)
|
||||
return eval->Invalid("import-tx-token-params-incorrect");
|
||||
}
|
||||
|
||||
|
||||
// Check burntx shows correct outputs hash
|
||||
// if (payoutsHash != SerializeHash(payouts)) // done in ImportCoin
|
||||
// return eval->Invalid("wrong-payouts");
|
||||
|
||||
|
||||
TxProof merkleBranchProof;
|
||||
std::vector<uint256> notaryTxids;
|
||||
|
||||
// Check proof confirms existance of burnTx
|
||||
if (proof.IsMerkleBranch(merkleBranchProof)) {
|
||||
uint256 target = merkleBranchProof.second.Exec(burnTx.GetHash());
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Eval::ImportCoin() momom target=" << target.GetHex() << " merkleBranchProof.first=" << merkleBranchProof.first.GetHex() << std::endl);
|
||||
if (!CheckMoMoM(merkleBranchProof.first, target)) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MoMoM check failed for importtx=" << importTx.GetHash().GetHex() << std::endl);
|
||||
return eval->Invalid("momom-check-fail");
|
||||
}
|
||||
}
|
||||
else if (proof.IsNotaryTxids(notaryTxids)) {
|
||||
if (!CheckNotariesApproval(burnTx.GetHash(), notaryTxids)) {
|
||||
return eval->Invalid("notaries-approval-check-fail");
|
||||
}
|
||||
}
|
||||
else {
|
||||
return eval->Invalid("invalid-import-proof");
|
||||
}
|
||||
|
||||
/* if (vimportOpret.begin()[0] == EVAL_TOKENS)
|
||||
return eval->Invalid("test-invalid-tokens-are-good!!");
|
||||
else
|
||||
return eval->Invalid("test-invalid-coins-are-good!!"); */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Eval::ImportCoin(const std::vector<uint8_t> params, const CTransaction &importTx, unsigned int nIn)
|
||||
{
|
||||
ImportProof proof;
|
||||
CTransaction burnTx;
|
||||
std::vector<CTxOut> payouts;
|
||||
CAmount txfee = 10000, amount;
|
||||
int32_t height, burnvout;
|
||||
std::vector<CPubKey> publishers;
|
||||
uint32_t targetCcid;
|
||||
std::string targetSymbol, srcaddr, destaddr, receipt, rawburntx;
|
||||
uint256 payoutsHash, bindtxid, burntxid;
|
||||
std::vector<uint8_t> rawproof;
|
||||
std::vector<uint256> txids;
|
||||
CPubKey destpub;
|
||||
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "Validating import tx..., txid=" << importTx.GetHash().GetHex() << std::endl);
|
||||
|
||||
if (strcmp(ASSETCHAINS_SYMBOL, "CFEKDIMXY6") == 0 && chainActive.Height() <= 44693)
|
||||
return true;
|
||||
|
||||
if (importTx.vout.size() < 2)
|
||||
return Invalid("too-few-vouts");
|
||||
// params
|
||||
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
|
||||
@@ -597,38 +684,47 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params,const CTransaction &impo
|
||||
// burn params
|
||||
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash, rawproof))
|
||||
return Invalid("invalid-burn-tx");
|
||||
// check burn amount
|
||||
{
|
||||
uint64_t burnAmount = burnTx.vout.back().nValue;
|
||||
if (burnAmount == 0)
|
||||
return Invalid("invalid-burn-amount");
|
||||
uint64_t totalOut = 0;
|
||||
for (int i=0; i<importTx.vout.size(); i++)
|
||||
totalOut += importTx.vout[i].nValue;
|
||||
if (totalOut > burnAmount || totalOut < burnAmount-txfee )
|
||||
return Invalid("payout-too-high-or-too-low");
|
||||
}
|
||||
|
||||
if (burnTx.vout.size() == 0)
|
||||
return Invalid("invalid-burn-tx-no-vouts");
|
||||
|
||||
// check burned normal amount >= import amount && burned amount <= import amount + txfee (extra txfee is for miners and relaying, see GetImportCoinValue() func)
|
||||
CAmount burnAmount = burnTx.vout.back().nValue;
|
||||
if (burnAmount == 0)
|
||||
return Invalid("invalid-burn-amount");
|
||||
CAmount totalOut = 0;
|
||||
for (auto v : importTx.vout)
|
||||
if (!v.scriptPubKey.IsPayToCryptoCondition())
|
||||
totalOut += v.nValue;
|
||||
if (totalOut > burnAmount || totalOut < burnAmount - txfee)
|
||||
return Invalid("payout-too-high-or-too-low");
|
||||
|
||||
// Check burntx shows correct outputs hash
|
||||
if (payoutsHash != SerializeHash(payouts))
|
||||
return Invalid("wrong-payouts");
|
||||
if (targetCcid < KOMODO_FIRSTFUNGIBLEID)
|
||||
return Invalid("chain-not-fungible");
|
||||
// Check proof confirms existance of burnTx
|
||||
|
||||
if ( targetCcid != 0xffffffff )
|
||||
{
|
||||
if ( targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol() )
|
||||
|
||||
if (targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol())
|
||||
return Invalid("importcoin-wrong-chain");
|
||||
uint256 target = proof.second.Exec(burnTx.GetHash());
|
||||
if (!CheckMoMoM(proof.first, target))
|
||||
return Invalid("momom-check-fail");
|
||||
|
||||
if (!CheckMigration(this, importTx, burnTx, payouts, proof, rawproof))
|
||||
return false; // eval->Invalid() is called in the func
|
||||
}
|
||||
else
|
||||
{
|
||||
TxProof merkleBranchProof;
|
||||
if (!proof.IsMerkleBranch(merkleBranchProof))
|
||||
return Invalid("invalid-import-proof-for-0xFFFFFFFF");
|
||||
|
||||
if ( targetSymbol == "BEAM" )
|
||||
{
|
||||
if ( ASSETCHAINS_BEAMPORT == 0 )
|
||||
return Invalid("BEAM-import-without-port");
|
||||
else if ( CheckBEAMimport(proof,rawproof,burnTx,payouts) < 0 )
|
||||
else if ( CheckBEAMimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
|
||||
return Invalid("BEAM-import-failure");
|
||||
}
|
||||
else if ( targetSymbol == "CODA" )
|
||||
@@ -642,16 +738,20 @@ bool Eval::ImportCoin(const std::vector<uint8_t> params,const CTransaction &impo
|
||||
{
|
||||
if ( ASSETCHAINS_SELFIMPORT != "PUBKEY" )
|
||||
return Invalid("PUBKEY-import-when-notPUBKEY");
|
||||
else if ( CheckPUBKEYimport(proof,rawproof,burnTx,payouts) < 0 )
|
||||
else if ( CheckPUBKEYimport(merkleBranchProof,rawproof,burnTx,payouts) < 0 )
|
||||
return Invalid("PUBKEY-import-failure");
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( targetSymbol != ASSETCHAINS_SELFIMPORT )
|
||||
return Invalid("invalid-gateway-import-coin");
|
||||
else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,height,burnvout,rawburntx,destpub)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,height,burnvout,rawburntx,destpub) < 0 )
|
||||
else if ( UnmarshalBurnTx(burnTx,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount)==0 || CheckGATEWAYimport(importTx,burnTx,targetSymbol,rawproof,bindtxid,publishers,txids,burntxid,height,burnvout,rawburntx,destpub,amount) < 0 )
|
||||
return Invalid("GATEWAY-import-failure");
|
||||
}
|
||||
}
|
||||
|
||||
// return Invalid("test-invalid");
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "Valid import tx! txid=" << importTx.GetHash().GetHex() << std::endl);
|
||||
|
||||
return Valid();
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
#define KMD_TADDR 0
|
||||
#define CC_MARKER_VALUE 10000
|
||||
|
||||
extern uint256 KOMODO_EARLYTXID;
|
||||
|
||||
CScript EncodeImportGatewayBindOpRet(uint8_t funcid,std::string coin,uint256 oracletxid,uint8_t M,uint8_t N,std::vector<CPubKey> importgatewaypubkeys,uint8_t taddr,uint8_t prefix,uint8_t prefix2,uint8_t wiftype)
|
||||
{
|
||||
CScript opret; uint8_t evalcode = EVAL_IMPORTGATEWAY;
|
||||
@@ -212,7 +214,7 @@ int64_t ImportGatewayVerify(char *refburnaddr,uint256 oracletxid,int32_t claimvo
|
||||
}
|
||||
if (std::find(txids.begin(), txids.end(), burntxid) == txids.end())
|
||||
{
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify invalid proof for this burntxid" << std::endl);
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << "ImportGatewayVerify invalid proof for this burntxid " << burntxid.GetHex() << std::endl);
|
||||
return 0;
|
||||
}
|
||||
if ( DecodeHexTx(tx,deposithex) != 0 )
|
||||
@@ -557,13 +559,19 @@ std::string ImportGatewayBind(uint64_t txfee,std::string coin,uint256 oracletxid
|
||||
return("");
|
||||
}
|
||||
|
||||
std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t claimvout,std::string rawburntx,std::vector<uint8_t>proof,CPubKey destpub)
|
||||
std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,std::string refcoin,uint256 burntxid,int32_t claimvout,std::string rawburntx,std::vector<uint8_t>proof,CPubKey destpub,int64_t amount)
|
||||
{
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()), burntx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
CTransaction bindtx; CPubKey mypk; uint256 oracletxid,merkleroot,mhash,hashBlock,txid; std::vector<CTxOut> vouts;
|
||||
int32_t i,m,n,numvouts; uint8_t M,N,taddr,prefix,prefix2,wiftype; std::string coin; struct CCcontract_info *cp,C;
|
||||
std::vector<CPubKey> pubkeys,publishers; std::vector<uint256> txids; char str[128],burnaddr[64]; int64_t amount;
|
||||
std::vector<CPubKey> pubkeys,publishers; std::vector<uint256> txids; char str[128],burnaddr[64];
|
||||
|
||||
if (KOMODO_EARLYTXID!=zeroid && bindtxid!=KOMODO_EARLYTXID)
|
||||
{
|
||||
CCerror = strprintf("CheckGATEWAYimport invalid import gateway. On this chain only valid import gateway is %s",KOMODO_EARLYTXID.GetHex());
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl);
|
||||
return("");
|
||||
}
|
||||
cp = CCinit(&C,EVAL_IMPORTGATEWAY);
|
||||
if ( txfee == 0 )
|
||||
txfee = 10000;
|
||||
@@ -572,7 +580,6 @@ std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,
|
||||
{
|
||||
return std::string("");
|
||||
}
|
||||
amount=burntx.vout[0].nValue;
|
||||
LOGSTREAM("importgateway",CCLOG_DEBUG1, stream << "ImportGatewayDeposit ht." << height << " " << refcoin << " " << (double)amount/COIN << " numpks." << (int32_t)pubkeys.size() << std::endl);
|
||||
if ( GetTransaction(bindtxid,bindtx,hashBlock,false) == 0 || (numvouts= bindtx.vout.size()) <= 0 )
|
||||
{
|
||||
@@ -611,18 +618,18 @@ std::string ImportGatewayDeposit(uint64_t txfee,uint256 bindtxid,int32_t height,
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << "burntxid." << burntxid.GetHex() << " m." << m << " of n." << n << std::endl);
|
||||
if ( merkleroot == zeroid || m < n/2 )
|
||||
{
|
||||
CCerror = strprintf("couldnt find merkleroot for ht.%d %s oracle.%s m.%d vs n.%d",height,coin.c_str(),uint256_str(str,oracletxid),m,n);
|
||||
CCerror = strprintf("couldnt find merkleroot for ht.%d %s oracle.%s m.%d vs n.%d",height,refcoin.c_str(),uint256_str(str,oracletxid),m,n);
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl);
|
||||
return("");
|
||||
}
|
||||
if ( ImportGatewayVerify(burnaddr,oracletxid,claimvout,coin,burntxid,rawburntx,proof,merkleroot,destpub,taddr,prefix,prefix2) != amount )
|
||||
if ( ImportGatewayVerify(burnaddr,oracletxid,claimvout,refcoin,burntxid,rawburntx,proof,merkleroot,destpub,taddr,prefix,prefix2) != amount )
|
||||
{
|
||||
CCerror = strprintf("burntxid didnt validate!");
|
||||
LOGSTREAM("importgateway",CCLOG_INFO, stream << CCerror << std::endl);
|
||||
return("");
|
||||
}
|
||||
vouts.push_back(CTxOut(amount,CScript() << ParseHex(HexStr(destpub)) << OP_CHECKSIG));
|
||||
burntx.vout.push_back(MakeBurnOutput((CAmount)amount,0xffffffff,refcoin,vouts,proof,bindtxid,publishers,txids,height,claimvout,rawburntx,destpub));
|
||||
burntx.vout.push_back(MakeBurnOutput((CAmount)amount,0xffffffff,refcoin,vouts,proof,bindtxid,publishers,txids,burntxid,height,claimvout,rawburntx,destpub,amount));
|
||||
std::vector<uint256> leaftxids;
|
||||
BitcoinGetProofMerkleRoot(proof, leaftxids);
|
||||
MerkleBranch newBranch(0, leaftxids);
|
||||
|
||||
@@ -1028,7 +1028,7 @@ UniValue OraclesList()
|
||||
{
|
||||
UniValue result(UniValue::VARR); std::vector<std::pair<CAddressIndexKey, CAmount> > addressIndex; struct CCcontract_info *cp,C; uint256 txid,hashBlock; CTransaction createtx; std::string name,description,format; char str[65];
|
||||
cp = CCinit(&C,EVAL_ORACLES);
|
||||
SetCCtxids(addressIndex,cp->normaladdr,true);
|
||||
SetCCtxids(addressIndex,cp->normaladdr,false);
|
||||
for (std::vector<std::pair<CAddressIndexKey, CAmount> >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++)
|
||||
{
|
||||
txid = it->first.txhash;
|
||||
|
||||
@@ -92,6 +92,7 @@ static CBlock CreateGenesisBlock(uint32_t nTime, const uint256& nNonce, const st
|
||||
void *chainparams_commandline(void *ptr);
|
||||
#include "komodo_defs.h"
|
||||
int32_t ASSETCHAINS_BLOCKTIME = 60;
|
||||
uint64_t ASSETCHAINS_NK[2];
|
||||
|
||||
const arith_uint256 maxUint = UintToArith256(uint256S("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"));
|
||||
|
||||
@@ -282,6 +283,12 @@ void *chainparams_commandline(void *ptr)
|
||||
mainParams.consensus.nPowTargetSpacing = ASSETCHAINS_BLOCKTIME;
|
||||
}
|
||||
mainParams.SetDefaultPort(ASSETCHAINS_P2PPORT);
|
||||
if ( ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0 )
|
||||
{
|
||||
//BOOST_STATIC_ASSERT(equihash_parameters_acceptable(ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]));
|
||||
mainParams.SetNValue(ASSETCHAINS_NK[0]);
|
||||
mainParams.SetKValue(ASSETCHAINS_NK[1]);
|
||||
}
|
||||
if ( ASSETCHAINS_RPCPORT == 0 )
|
||||
ASSETCHAINS_RPCPORT = ASSETCHAINS_P2PPORT + 1;
|
||||
mainParams.pchMessageStart[0] = ASSETCHAINS_MAGIC & 0xff;
|
||||
|
||||
@@ -119,6 +119,8 @@ public:
|
||||
|
||||
void SetDefaultPort(uint16_t port) { nDefaultPort = port; }
|
||||
void SetCheckpointData(CCheckpointData checkpointData);
|
||||
void SetNValue(uint64_t n) { nEquihashN = n; }
|
||||
void SetKValue(uint64_t k) { nEquihashK = k; }
|
||||
|
||||
//void setnonce(uint32_t nonce) { memcpy(&genesis.nNonce,&nonce,sizeof(nonce)); }
|
||||
//void settimestamp(uint32_t timestamp) { genesis.nTime = timestamp; }
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
#include "importcoin.h"
|
||||
#include "main.h"
|
||||
#include "notarisationdb.h"
|
||||
#include "merkleblock.h"
|
||||
|
||||
#include "cc/CCinclude.h"
|
||||
|
||||
/*
|
||||
* The crosschain workflow.
|
||||
@@ -64,11 +67,12 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh
|
||||
if (kmdHeight < 0 || kmdHeight > chainActive.Height())
|
||||
return uint256();
|
||||
|
||||
int seenOwnNotarisations = 0;
|
||||
int seenOwnNotarisations = 0, i = 0;
|
||||
|
||||
int authority = GetSymbolAuthority(symbol);
|
||||
std::set<uint256> tmp_moms;
|
||||
|
||||
for (int i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
|
||||
for (i=0; i<NOTARISATION_SCAN_LIMIT_BLOCKS; i++) {
|
||||
if (i > kmdHeight) break;
|
||||
NotarisationsInBlock notarisations;
|
||||
uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock;
|
||||
@@ -82,17 +86,17 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh
|
||||
seenOwnNotarisations++;
|
||||
if (seenOwnNotarisations == 1)
|
||||
destNotarisationTxid = nota.first;
|
||||
else if (seenOwnNotarisations == 2)
|
||||
else if (seenOwnNotarisations == 7)
|
||||
goto end;
|
||||
//break;
|
||||
}
|
||||
}
|
||||
|
||||
if (seenOwnNotarisations == 1) {
|
||||
if (seenOwnNotarisations >= 1) {
|
||||
BOOST_FOREACH(Notarisation& nota, notarisations) {
|
||||
if (GetSymbolAuthority(nota.second.symbol) == authority)
|
||||
if (nota.second.ccId == targetCCid) {
|
||||
moms.push_back(nota.second.MoM);
|
||||
tmp_moms.insert(nota.second.MoM);
|
||||
//fprintf(stderr, "added mom: %s\n",nota.second.MoM.GetHex().data());
|
||||
}
|
||||
}
|
||||
@@ -105,6 +109,10 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh
|
||||
return uint256();
|
||||
|
||||
end:
|
||||
// add set to vector. Set makes sure there are no dupes included.
|
||||
moms.clear();
|
||||
std::copy(tmp_moms.begin(), tmp_moms.end(), std::back_inserter(moms));
|
||||
//fprintf(stderr, "SeenOwnNotarisations.%i moms.size.%li blocks scanned.%i\n",seenOwnNotarisations, moms.size(), i);
|
||||
return GetMerkleRoot(moms);
|
||||
}
|
||||
|
||||
@@ -138,7 +146,7 @@ int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &fou
|
||||
|
||||
/* On KMD */
|
||||
TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid,
|
||||
const TxProof assetChainProof)
|
||||
const TxProof assetChainProof, int32_t offset)
|
||||
{
|
||||
/*
|
||||
* Here we are given a proof generated by an assetchain A which goes from given txid to
|
||||
@@ -173,6 +181,9 @@ TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_
|
||||
kmdHeight = ScanNotarisationsFromHeight(kmdHeight, isTarget, nota);
|
||||
if (!kmdHeight)
|
||||
throw std::runtime_error("Cannot find notarisation for target inclusive of source");
|
||||
|
||||
if ( offset != 0 )
|
||||
kmdHeight += offset;
|
||||
|
||||
// Get MoMs for kmd height and symbol
|
||||
std::vector<uint256> moms;
|
||||
@@ -219,24 +230,27 @@ cont:
|
||||
* Takes an importTx that has proof leading to assetchain root
|
||||
* and extends proof to cross chain root
|
||||
*/
|
||||
void CompleteImportTransaction(CTransaction &importTx)
|
||||
void CompleteImportTransaction(CTransaction &importTx, int32_t offset)
|
||||
{
|
||||
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; std::vector<uint8_t> rawproof;
|
||||
ImportProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; std::vector<uint8_t> rawproof;
|
||||
if (!UnmarshalImportTx(importTx, proof, burnTx, payouts))
|
||||
throw std::runtime_error("Couldn't parse importTx");
|
||||
throw std::runtime_error("Couldn't unmarshal importTx");
|
||||
|
||||
std::string targetSymbol;
|
||||
uint32_t targetCCid;
|
||||
uint256 payoutsHash;
|
||||
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof))
|
||||
throw std::runtime_error("Couldn't parse burnTx");
|
||||
throw std::runtime_error("Couldn't unmarshal burnTx");
|
||||
|
||||
proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof);
|
||||
TxProof merkleBranch;
|
||||
if( !proof.IsMerkleBranch(merkleBranch) )
|
||||
throw std::runtime_error("Incorrect import tx proof");
|
||||
TxProof newMerkleBranch = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, merkleBranch, offset);
|
||||
ImportProof newProof(newMerkleBranch);
|
||||
|
||||
importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
|
||||
importTx = MakeImportCoinTransaction(newProof, burnTx, payouts);
|
||||
}
|
||||
|
||||
|
||||
bool IsSameAssetChain(const Notarisation ¬a) {
|
||||
return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0;
|
||||
};
|
||||
@@ -298,6 +312,111 @@ bool CheckMoMoM(uint256 kmdNotarisationHash, uint256 momom)
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Check notaries approvals for the txoutproofs of burn tx
|
||||
* (alternate check if MoMoM check has failed)
|
||||
* Params:
|
||||
* burntxid - txid of burn tx on the source chain
|
||||
* rawproof - array of txids of notaries' proofs
|
||||
*/
|
||||
bool CheckNotariesApproval(uint256 burntxid, const std::vector<uint256> & notaryTxids)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
// get notaries:
|
||||
uint8_t notaries_pubkeys[64][33];
|
||||
std::vector< std::vector<uint8_t> > alreadySigned;
|
||||
|
||||
//unmarshal notaries approval txids
|
||||
for(auto notarytxid : notaryTxids ) {
|
||||
EvalRef eval;
|
||||
CBlockIndex block;
|
||||
CTransaction notarytx; // tx with notary approval of txproof existence
|
||||
|
||||
// get notary approval tx
|
||||
if (eval->GetTxConfirmed(notarytxid, notarytx, block)) {
|
||||
|
||||
std::vector<uint8_t> vopret;
|
||||
if (!notarytx.vout.empty() && GetOpReturnData(notarytx.vout.back().scriptPubKey, vopret)) {
|
||||
std::vector<uint8_t> txoutproof;
|
||||
|
||||
if (E_UNMARSHAL(vopret, ss >> txoutproof)) {
|
||||
CMerkleBlock merkleBlock;
|
||||
std::vector<uint256> prooftxids;
|
||||
// extract block's merkle tree
|
||||
if (E_UNMARSHAL(txoutproof, ss >> merkleBlock)) {
|
||||
|
||||
// extract proven txids:
|
||||
merkleBlock.txn.ExtractMatches(prooftxids);
|
||||
if (merkleBlock.txn.ExtractMatches(prooftxids) != merkleBlock.header.hashMerkleRoot || // check block merkle root is correct
|
||||
std::find(prooftxids.begin(), prooftxids.end(), burntxid) != prooftxids.end()) { // check burn txid is in proven txids list
|
||||
|
||||
if (komodo_notaries(notaries_pubkeys, block.GetHeight(), block.GetBlockTime()) >= 0) {
|
||||
// check it is a notary who signed approved tx:
|
||||
int i;
|
||||
for (i = 0; i < sizeof(notaries_pubkeys) / sizeof(notaries_pubkeys[0]); i++) {
|
||||
std::vector<uint8_t> vnotarypubkey(notaries_pubkeys[i], notaries_pubkeys[i] + 33);
|
||||
#ifdef TESTMODE
|
||||
char test_notary_pubkey_hex[] = "029fa302968bbae81f41983d2ec20445557b889d31227caec5d910d19b7510ef86";
|
||||
uint8_t test_notary_pubkey33[33];
|
||||
decode_hex(test_notary_pubkey33, 33, test_notary_pubkey_hex);
|
||||
#endif
|
||||
if (CheckVinPubKey(notarytx, 0, notaries_pubkeys[i]) // is signed by a notary?
|
||||
&& std::find(alreadySigned.begin(), alreadySigned.end(), vnotarypubkey) == alreadySigned.end() // check if notary not re-used
|
||||
#ifdef TESTMODE
|
||||
|| CheckVinPubKey(notarytx, 0, test_notary_pubkey33) // test
|
||||
#endif
|
||||
)
|
||||
{
|
||||
alreadySigned.push_back(vnotarypubkey);
|
||||
count++;
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "CheckNotariesApproval() notary approval checked, count=" << count << std::endl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == sizeof(notaries_pubkeys) / sizeof(notaries_pubkeys[0]))
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG1, stream << "CheckNotariesApproval() txproof not signed by a notary or reused" << std::endl);
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() cannot get current notaries pubkeys" << std::endl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() burntxid not found in txoutproof or incorrect txoutproof" << std::endl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() could not unmarshal merkleBlock" << std::endl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() could not unmarshal txoutproof" << std::endl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() no opret in the notary tx" << std::endl);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() could not load notary tx" << std::endl);
|
||||
}
|
||||
}
|
||||
|
||||
bool retcode;
|
||||
#ifdef TESTMODE
|
||||
if (count < 1) { // 1 for test
|
||||
#else
|
||||
if (count < 5) {
|
||||
#endif
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "CheckNotariesApproval() not enough signed notary transactions=" << count << std::endl);
|
||||
retcode = false;
|
||||
}
|
||||
else
|
||||
retcode = true;
|
||||
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* On assetchain
|
||||
|
||||
@@ -38,11 +38,11 @@ TxProof GetAssetchainProof(uint256 hash,CTransaction burnTx);
|
||||
uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight,
|
||||
std::vector<uint256> &moms, uint256 &destNotarisationTxid);
|
||||
TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid,
|
||||
const TxProof assetChainProof);
|
||||
void CompleteImportTransaction(CTransaction &importTx);
|
||||
const TxProof assetChainProof,int32_t offset);
|
||||
void CompleteImportTransaction(CTransaction &importTx,int32_t offset);
|
||||
|
||||
/* On assetchain */
|
||||
bool CheckMoMoM(uint256 kmdNotarisationHash, uint256 momom);
|
||||
|
||||
bool CheckNotariesApproval(uint256 burntxid, const std::vector<uint256> & notaryTxids);
|
||||
|
||||
#endif /* CROSSCHAIN_H */
|
||||
|
||||
@@ -54,33 +54,84 @@
|
||||
#define __BYTE_ORDER BYTE_ORDER
|
||||
#endif
|
||||
*/
|
||||
|
||||
static EhSolverCancelledException solver_cancelled;
|
||||
|
||||
int8_t ZeroizeUnusedBits(size_t N, unsigned char* hash, size_t hLen)
|
||||
{
|
||||
uint8_t rem = N % 8;
|
||||
if (rem)
|
||||
{
|
||||
// clear lowest 8-rem bits
|
||||
const size_t step = GetSizeInBytes(N);
|
||||
for (size_t i = step - 1; i < hLen; i += step)
|
||||
{
|
||||
uint8_t b = 0xff << (8-rem);
|
||||
hash[i] &= b;
|
||||
}
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
template<unsigned int N, unsigned int K>
|
||||
int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
|
||||
{
|
||||
uint32_t le_N = htole32(N);
|
||||
uint32_t le_K = htole32(K);
|
||||
|
||||
unsigned char personalization[crypto_generichash_blake2b_PERSONALBYTES] = {};
|
||||
memcpy(personalization, "ZcashPoW", 8);
|
||||
if ( ASSETCHAINS_NK[0] == 0 && ASSETCHAINS_NK[1] == 0 )
|
||||
memcpy(personalization, "ZcashPoW", 8);
|
||||
else
|
||||
memcpy(personalization, "NandKPoW", 8);
|
||||
memcpy(personalization+8, &le_N, 4);
|
||||
memcpy(personalization+12, &le_K, 4);
|
||||
|
||||
const uint8_t outlen = (512 / N) * GetSizeInBytes(N);
|
||||
|
||||
BOOST_STATIC_ASSERT(!((!outlen) || (outlen > BLAKE2B_OUTBYTES)));
|
||||
return crypto_generichash_blake2b_init_salt_personal(&base_state,
|
||||
NULL, 0, // No key.
|
||||
(512/N)*N/8,
|
||||
outlen,
|
||||
NULL, // No salt.
|
||||
personalization);
|
||||
}
|
||||
|
||||
void GenerateHash(const eh_HashState& base_state, eh_index g,
|
||||
unsigned char* hash, size_t hLen)
|
||||
unsigned char* hash, size_t hLen, size_t N)
|
||||
{
|
||||
eh_HashState state;
|
||||
state = base_state;
|
||||
eh_index lei = htole32(g);
|
||||
crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei,
|
||||
sizeof(eh_index));
|
||||
crypto_generichash_blake2b_final(&state, hash, hLen);
|
||||
if ( ASSETCHAINS_NK[0] == 0 && ASSETCHAINS_NK[1] == 0 )
|
||||
{
|
||||
eh_HashState state;
|
||||
state = base_state;
|
||||
eh_index lei = htole32(g);
|
||||
crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei,
|
||||
sizeof(eh_index));
|
||||
crypto_generichash_blake2b_final(&state, hash, hLen);
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t myHash[16] = {0};
|
||||
uint32_t startIndex = g & 0xFFFFFFF0;
|
||||
|
||||
for (uint32_t g2 = startIndex; g2 <= g; g2++) {
|
||||
uint32_t tmpHash[16] = {0};
|
||||
|
||||
eh_HashState state;
|
||||
state = base_state;
|
||||
eh_index lei = htole32(g2);
|
||||
crypto_generichash_blake2b_update(&state, (const unsigned char*) &lei,
|
||||
sizeof(eh_index));
|
||||
|
||||
crypto_generichash_blake2b_final(&state, (unsigned char*)&tmpHash[0], static_cast<uint8_t>(hLen));
|
||||
|
||||
for (uint32_t idx = 0; idx < 16; idx++) myHash[idx] += tmpHash[idx];
|
||||
}
|
||||
|
||||
memcpy(hash, &myHash[0], hLen);
|
||||
ZeroizeUnusedBits(N, hash, hLen);
|
||||
}
|
||||
}
|
||||
|
||||
void ExpandArray(const unsigned char* in, size_t in_len,
|
||||
@@ -88,7 +139,7 @@ void ExpandArray(const unsigned char* in, size_t in_len,
|
||||
size_t bit_len, size_t byte_pad)
|
||||
{
|
||||
assert(bit_len >= 8);
|
||||
assert(8*sizeof(uint32_t) >= 7+bit_len);
|
||||
assert(8*sizeof(uint32_t) >= bit_len);
|
||||
|
||||
size_t out_width { (bit_len+7)/8 + byte_pad };
|
||||
assert(out_len == 8*out_width*in_len/bit_len);
|
||||
@@ -131,10 +182,10 @@ void CompressArray(const unsigned char* in, size_t in_len,
|
||||
size_t bit_len, size_t byte_pad)
|
||||
{
|
||||
assert(bit_len >= 8);
|
||||
assert(8*sizeof(uint32_t) >= 7+bit_len);
|
||||
assert(8*sizeof(uint32_t) >= bit_len);
|
||||
|
||||
size_t in_width { (bit_len+7)/8 + byte_pad };
|
||||
assert(out_len == bit_len*in_len/(8*in_width));
|
||||
assert(out_len == (bit_len*in_len/in_width + 7)/8);
|
||||
|
||||
uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 };
|
||||
|
||||
@@ -148,17 +199,23 @@ void CompressArray(const unsigned char* in, size_t in_len,
|
||||
// When we have fewer than 8 bits left in the accumulator, read the next
|
||||
// input element.
|
||||
if (acc_bits < 8) {
|
||||
if (j < in_len) {
|
||||
acc_value = acc_value << bit_len;
|
||||
for (size_t x = byte_pad; x < in_width; x++) {
|
||||
acc_value = acc_value | (
|
||||
(
|
||||
// Apply bit_len_mask across byte boundaries
|
||||
in[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF)
|
||||
) << (8*(in_width-x-1))); // Big-endian
|
||||
in[j + x] & ((bit_len_mask >> (8 * (in_width - x - 1))) & 0xFF)
|
||||
) << (8 * (in_width - x - 1))); // Big-endian
|
||||
}
|
||||
j += in_width;
|
||||
acc_bits += bit_len;
|
||||
}
|
||||
else {
|
||||
acc_value <<= 8 - acc_bits;
|
||||
acc_bits += 8 - acc_bits;;
|
||||
}
|
||||
}
|
||||
|
||||
acc_bits -= 8;
|
||||
out[i] = (acc_value >> acc_bits) & 0xFF;
|
||||
@@ -207,7 +264,7 @@ std::vector<eh_index> GetIndicesFromMinimal(std::vector<unsigned char> minimal,
|
||||
ExpandArray(minimal.data(), minimal.size(),
|
||||
array.data(), lenIndices, cBitLen+1, bytePad);
|
||||
std::vector<eh_index> ret;
|
||||
for (int i = 0; i < lenIndices; i += sizeof(eh_index)) {
|
||||
for (size_t i = 0; i < lenIndices; i += sizeof(eh_index)) {
|
||||
ret.push_back(ArrayToEhIndex(array.data()+i));
|
||||
}
|
||||
return ret;
|
||||
@@ -221,7 +278,7 @@ std::vector<unsigned char> GetMinimalFromIndices(std::vector<eh_index> indices,
|
||||
size_t minLen { (cBitLen+1)*lenIndices/(8*sizeof(eh_index)) };
|
||||
size_t bytePad { sizeof(eh_index) - ((cBitLen+1)+7)/8 };
|
||||
std::vector<unsigned char> array(lenIndices);
|
||||
for (int i = 0; i < indices.size(); i++) {
|
||||
for (size_t i = 0; i < indices.size(); i++) {
|
||||
EhIndexToArray(indices[i], array.data()+(i*sizeof(eh_index)));
|
||||
}
|
||||
std::vector<unsigned char> ret(minLen);
|
||||
@@ -254,12 +311,12 @@ FullStepRow<WIDTH>::FullStepRow(const unsigned char* hashIn, size_t hInLen,
|
||||
}
|
||||
|
||||
template<size_t WIDTH> template<size_t W>
|
||||
FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim) :
|
||||
FullStepRow<WIDTH>::FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, size_t trim) :
|
||||
StepRow<WIDTH> {a}
|
||||
{
|
||||
assert(len+lenIndices <= W);
|
||||
assert(len-trim+(2*lenIndices) <= WIDTH);
|
||||
for (int i = trim; i < len; i++)
|
||||
for (size_t i = trim; i < len; i++)
|
||||
hash[i-trim] = a.hash[i] ^ b.hash[i];
|
||||
if (a.IndicesBefore(b, len, lenIndices)) {
|
||||
std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
|
||||
@@ -281,7 +338,7 @@ template<size_t WIDTH>
|
||||
bool StepRow<WIDTH>::IsZero(size_t len)
|
||||
{
|
||||
// This doesn't need to be constant time.
|
||||
for (int i = 0; i < len; i++) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
if (hash[i] != 0)
|
||||
return false;
|
||||
}
|
||||
@@ -301,10 +358,10 @@ std::vector<unsigned char> FullStepRow<WIDTH>::GetIndices(size_t len, size_t len
|
||||
}
|
||||
|
||||
template<size_t WIDTH>
|
||||
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l)
|
||||
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, size_t l)
|
||||
{
|
||||
// This doesn't need to be constant time.
|
||||
for (int j = 0; j < l; j++) {
|
||||
for (size_t j = 0; j < l; j++) {
|
||||
if (a.hash[j] != b.hash[j])
|
||||
return false;
|
||||
}
|
||||
@@ -326,7 +383,7 @@ TruncatedStepRow<WIDTH>::TruncatedStepRow(const TruncatedStepRow<W>& a, const Tr
|
||||
{
|
||||
assert(len+lenIndices <= W);
|
||||
assert(len-trim+(2*lenIndices) <= WIDTH);
|
||||
for (int i = trim; i < len; i++)
|
||||
for (size_t i = static_cast<size_t>(trim); i < len; i++)
|
||||
hash[i-trim] = a.hash[i] ^ b.hash[i];
|
||||
if (a.IndicesBefore(b, len, lenIndices)) {
|
||||
std::copy(a.hash+len, a.hash+len+lenIndices, hash+len-trim);
|
||||
@@ -355,10 +412,10 @@ std::shared_ptr<eh_trunc> TruncatedStepRow<WIDTH>::GetTruncatedIndices(size_t le
|
||||
#ifdef ENABLE_MINING
|
||||
template<unsigned int N, unsigned int K>
|
||||
bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled)
|
||||
{
|
||||
eh_index init_size { 1 << (CollisionBitLength + 1) };
|
||||
eh_index init_size { 1U << (CollisionBitLength + 1) };
|
||||
|
||||
// 1) Generate first list
|
||||
LogPrint("pow", "Generating first list\n");
|
||||
@@ -368,16 +425,16 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
|
||||
X.reserve(init_size);
|
||||
unsigned char tmpHash[HashOutput];
|
||||
for (eh_index g = 0; X.size() < init_size; g++) {
|
||||
GenerateHash(base_state, g, tmpHash, HashOutput);
|
||||
GenerateHash(base_state, g, tmpHash, HashOutput, N);
|
||||
for (eh_index i = 0; i < IndicesPerHashOutput && X.size() < init_size; i++) {
|
||||
X.emplace_back(tmpHash+(i*N/8), N/8, HashLength,
|
||||
CollisionBitLength, (g*IndicesPerHashOutput)+i);
|
||||
X.emplace_back(tmpHash+(i*GetSizeInBytes(N)), GetSizeInBytes(N), HashLength,
|
||||
CollisionBitLength, static_cast<int>(g*IndicesPerHashOutput)+i);
|
||||
}
|
||||
if (cancelled(ListGeneration)) throw solver_cancelled;
|
||||
}
|
||||
|
||||
// 3) Repeat step 2 until 2n/(k+1) bits remain
|
||||
for (int r = 1; r < K && X.size() > 0; r++) {
|
||||
for (unsigned int r = 1; r < K && X.size() > 0; r++) {
|
||||
LogPrint("pow", "Round %d:\n", r);
|
||||
// 2a) Sort the list
|
||||
LogPrint("pow", "- Sorting list\n");
|
||||
@@ -385,20 +442,20 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
|
||||
if (cancelled(ListSorting)) throw solver_cancelled;
|
||||
|
||||
LogPrint("pow", "- Finding collisions\n");
|
||||
int i = 0;
|
||||
int posFree = 0;
|
||||
size_t i = 0;
|
||||
size_t posFree = 0;
|
||||
std::vector<FullStepRow<FullWidth>> Xc;
|
||||
while (i < X.size() - 1) {
|
||||
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
|
||||
int j = 1;
|
||||
size_t j = 1;
|
||||
while (i+j < X.size() &&
|
||||
HasCollision(X[i], X[i+j], CollisionByteLength)) {
|
||||
j++;
|
||||
}
|
||||
|
||||
// 2c) Calculate tuples (X_i ^ X_j, (i, j))
|
||||
for (int l = 0; l < j - 1; l++) {
|
||||
for (int m = l + 1; m < j; m++) {
|
||||
for (size_t l = 0; l < j - 1; l++) {
|
||||
for (size_t m = l + 1; m < j; m++) {
|
||||
if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
|
||||
Xc.emplace_back(X[i+l], X[i+m], hashLen, lenIndices, CollisionByteLength);
|
||||
}
|
||||
@@ -442,16 +499,16 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
|
||||
std::sort(X.begin(), X.end(), CompareSR(hashLen));
|
||||
if (cancelled(FinalSorting)) throw solver_cancelled;
|
||||
LogPrint("pow", "- Finding collisions\n");
|
||||
int i = 0;
|
||||
size_t i = 0;
|
||||
while (i < X.size() - 1) {
|
||||
int j = 1;
|
||||
size_t j = 1;
|
||||
while (i+j < X.size() &&
|
||||
HasCollision(X[i], X[i+j], hashLen)) {
|
||||
j++;
|
||||
}
|
||||
|
||||
for (int l = 0; l < j - 1; l++) {
|
||||
for (int m = l + 1; m < j; m++) {
|
||||
for (size_t l = 0; l < j - 1; l++) {
|
||||
for (size_t m = l + 1; m < j; m++) {
|
||||
FullStepRow<FinalFullWidth> res(X[i+l], X[i+m], hashLen, lenIndices, 0);
|
||||
if (DistinctIndices(X[i+l], X[i+m], hashLen, lenIndices)) {
|
||||
auto soln = res.GetIndices(hashLen, 2*lenIndices, CollisionBitLength);
|
||||
@@ -475,20 +532,21 @@ bool Equihash<N,K>::BasicSolve(const eh_HashState& base_state,
|
||||
template<size_t WIDTH>
|
||||
void CollideBranches(std::vector<FullStepRow<WIDTH>>& X, const size_t hlen, const size_t lenIndices, const unsigned int clen, const unsigned int ilen, const eh_trunc lt, const eh_trunc rt)
|
||||
{
|
||||
int i = 0;
|
||||
int posFree = 0;
|
||||
size_t i = 0;
|
||||
size_t posFree = 0;
|
||||
assert(X.size() > 0);
|
||||
std::vector<FullStepRow<WIDTH>> Xc;
|
||||
while (i < X.size() - 1) {
|
||||
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
|
||||
int j = 1;
|
||||
size_t j = 1;
|
||||
while (i+j < X.size() &&
|
||||
HasCollision(X[i], X[i+j], clen)) {
|
||||
j++;
|
||||
}
|
||||
|
||||
// 2c) Calculate tuples (X_i ^ X_j, (i, j))
|
||||
for (int l = 0; l < j - 1; l++) {
|
||||
for (int m = l + 1; m < j; m++) {
|
||||
for (size_t l = 0; l < j - 1; l++) {
|
||||
for (size_t m = l + 1; m < j; m++) {
|
||||
if (DistinctIndices(X[i+l], X[i+m], hlen, lenIndices)) {
|
||||
if (IsValidBranch(X[i+l], hlen, ilen, lt) && IsValidBranch(X[i+m], hlen, ilen, rt)) {
|
||||
Xc.emplace_back(X[i+l], X[i+m], hlen, lenIndices, clen);
|
||||
@@ -526,10 +584,10 @@ void CollideBranches(std::vector<FullStepRow<WIDTH>>& X, const size_t hlen, cons
|
||||
|
||||
template<unsigned int N, unsigned int K>
|
||||
bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled)
|
||||
{
|
||||
eh_index init_size { 1 << (CollisionBitLength + 1) };
|
||||
eh_index init_size { 1U << (CollisionBitLength + 1) };
|
||||
eh_index recreate_size { UntruncateIndex(1, 0, CollisionBitLength + 1) };
|
||||
|
||||
// First run the algorithm with truncated indices
|
||||
@@ -547,16 +605,16 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
Xt.reserve(init_size);
|
||||
unsigned char tmpHash[HashOutput];
|
||||
for (eh_index g = 0; Xt.size() < init_size; g++) {
|
||||
GenerateHash(base_state, g, tmpHash, HashOutput);
|
||||
GenerateHash(base_state, g, tmpHash, HashOutput, N);
|
||||
for (eh_index i = 0; i < IndicesPerHashOutput && Xt.size() < init_size; i++) {
|
||||
Xt.emplace_back(tmpHash+(i*N/8), N/8, HashLength, CollisionBitLength,
|
||||
(g*IndicesPerHashOutput)+i, CollisionBitLength + 1);
|
||||
Xt.emplace_back(tmpHash+(i*GetSizeInBytes(N)), GetSizeInBytes(N), HashLength, CollisionBitLength,
|
||||
static_cast<eh_index>(g*IndicesPerHashOutput)+i, static_cast<unsigned int>(CollisionBitLength + 1));
|
||||
}
|
||||
if (cancelled(ListGeneration)) throw solver_cancelled;
|
||||
}
|
||||
|
||||
// 3) Repeat step 2 until 2n/(k+1) bits remain
|
||||
for (int r = 1; r < K && Xt.size() > 0; r++) {
|
||||
for (unsigned int r = 1; r < K && Xt.size() > 0; r++) {
|
||||
LogPrint("pow", "Round %d:\n", r);
|
||||
// 2a) Sort the list
|
||||
LogPrint("pow", "- Sorting list\n");
|
||||
@@ -564,21 +622,21 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
if (cancelled(ListSorting)) throw solver_cancelled;
|
||||
|
||||
LogPrint("pow", "- Finding collisions\n");
|
||||
int i = 0;
|
||||
int posFree = 0;
|
||||
size_t i = 0;
|
||||
size_t posFree = 0;
|
||||
std::vector<TruncatedStepRow<TruncatedWidth>> Xc;
|
||||
while (i < Xt.size() - 1) {
|
||||
// 2b) Find next set of unordered pairs with collisions on the next n/(k+1) bits
|
||||
int j = 1;
|
||||
size_t j = 1;
|
||||
while (i+j < Xt.size() &&
|
||||
HasCollision(Xt[i], Xt[i+j], CollisionByteLength)) {
|
||||
j++;
|
||||
}
|
||||
|
||||
// 2c) Calculate tuples (X_i ^ X_j, (i, j))
|
||||
bool checking_for_zero = (i == 0 && Xt[0].IsZero(hashLen));
|
||||
for (int l = 0; l < j - 1; l++) {
|
||||
for (int m = l + 1; m < j; m++) {
|
||||
//bool checking_for_zero = (i == 0 && Xt[0].IsZero(hashLen));
|
||||
for (size_t l = 0; l < j - 1; l++) {
|
||||
for (size_t m = l + 1; m < j; m++) {
|
||||
// We truncated, so don't check for distinct indices here
|
||||
TruncatedStepRow<TruncatedWidth> Xi {Xt[i+l], Xt[i+m],
|
||||
hashLen, lenIndices,
|
||||
@@ -628,16 +686,16 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
std::sort(Xt.begin(), Xt.end(), CompareSR(hashLen));
|
||||
if (cancelled(FinalSorting)) throw solver_cancelled;
|
||||
LogPrint("pow", "- Finding collisions\n");
|
||||
int i = 0;
|
||||
size_t i = 0;
|
||||
while (i < Xt.size() - 1) {
|
||||
int j = 1;
|
||||
size_t j = 1;
|
||||
while (i+j < Xt.size() &&
|
||||
HasCollision(Xt[i], Xt[i+j], hashLen)) {
|
||||
j++;
|
||||
}
|
||||
|
||||
for (int l = 0; l < j - 1; l++) {
|
||||
for (int m = l + 1; m < j; m++) {
|
||||
for (size_t l = 0; l < j - 1; l++) {
|
||||
for (size_t m = l + 1; m < j; m++) {
|
||||
TruncatedStepRow<FinalTruncatedWidth> res(Xt[i+l], Xt[i+m],
|
||||
hashLen, lenIndices, 0);
|
||||
auto soln = res.GetTruncatedIndices(hashLen, 2*lenIndices);
|
||||
@@ -676,10 +734,10 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
eh_index newIndex { UntruncateIndex(partialSoln.get()[i], j, CollisionBitLength + 1) };
|
||||
if (j == 0 || newIndex % IndicesPerHashOutput == 0) {
|
||||
GenerateHash(base_state, newIndex/IndicesPerHashOutput,
|
||||
tmpHash, HashOutput);
|
||||
tmpHash, HashOutput, N);
|
||||
}
|
||||
icv.emplace_back(tmpHash+((newIndex % IndicesPerHashOutput) * N/8),
|
||||
N/8, HashLength, CollisionBitLength, newIndex);
|
||||
icv.emplace_back(tmpHash+((newIndex % IndicesPerHashOutput) * GetSizeInBytes(N)),
|
||||
GetSizeInBytes(N), HashLength, CollisionBitLength, newIndex);
|
||||
if (cancelled(PartialGeneration)) throw solver_cancelled;
|
||||
}
|
||||
boost::optional<std::vector<FullStepRow<FinalFullWidth>>> ic = icv;
|
||||
@@ -697,7 +755,7 @@ bool Equihash<N,K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
ic->insert(ic->end(), X[r]->begin(), X[r]->end());
|
||||
std::sort(ic->begin(), ic->end(), CompareSR(hashLen));
|
||||
if (cancelled(PartialSorting)) throw solver_cancelled;
|
||||
size_t lti = rti-(1<<r);
|
||||
size_t lti = rti-(static_cast<size_t>(1)<<r);
|
||||
CollideBranches(*ic, hashLen, lenIndices,
|
||||
CollisionByteLength,
|
||||
CollisionBitLength + 1,
|
||||
@@ -760,16 +818,16 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
|
||||
X.reserve(1 << K);
|
||||
unsigned char tmpHash[HashOutput];
|
||||
for (eh_index i : GetIndicesFromMinimal(soln, CollisionBitLength)) {
|
||||
GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput);
|
||||
X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * N/8),
|
||||
N/8, HashLength, CollisionBitLength, i);
|
||||
GenerateHash(base_state, i/IndicesPerHashOutput, tmpHash, HashOutput, N);
|
||||
X.emplace_back(tmpHash+((i % IndicesPerHashOutput) * GetSizeInBytes(N)),
|
||||
GetSizeInBytes(N), HashLength, CollisionBitLength, i);
|
||||
}
|
||||
|
||||
size_t hashLen = HashLength;
|
||||
size_t lenIndices = sizeof(eh_index);
|
||||
while (X.size() > 1) {
|
||||
std::vector<FullStepRow<FinalFullWidth>> Xc;
|
||||
for (int i = 0; i < X.size(); i += 2) {
|
||||
for (size_t i = 0; i < X.size(); i += 2) {
|
||||
if (!HasCollision(X[i], X[i+1], CollisionByteLength)) {
|
||||
LogPrint("pow", "Invalid solution: invalid collision length between StepRows\n");
|
||||
LogPrint("pow", "X[i] = %s\n", X[i].GetHex(hashLen));
|
||||
@@ -795,50 +853,74 @@ bool Equihash<N,K>::IsValidSolution(const eh_HashState& base_state, std::vector<
|
||||
return X[0].IsZero(hashLen);
|
||||
}
|
||||
|
||||
// Explicit instantiations for Equihash<96,3>
|
||||
template int Equihash<96,3>::InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
template bool Equihash<96,3>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<96,3>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<96,3>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
|
||||
// Explicit instantiations for Equihash<200,9>
|
||||
template int Equihash<200,9>::InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
template bool Equihash<200,9>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<200,9>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<200,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
|
||||
// Explicit instantiations for Equihash<96,5>
|
||||
template int Equihash<96,5>::InitialiseState(eh_HashState& base_state);
|
||||
|
||||
// Explicit instantiations for Equihash<96,3>
|
||||
template int Equihash<150,5>::InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
template bool Equihash<96,5>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
template bool Equihash<150,5>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<96,5>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
template bool Equihash<150,5>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<96,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
template bool Equihash<150,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
|
||||
// Explicit instantiations for Equihash<48,5>
|
||||
template int Equihash<144,5>::InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
template bool Equihash<144,5>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<144,5>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<144,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
|
||||
// Explicit instantiations for Equihash<96,5>
|
||||
template int Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
template bool Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<ASSETCHAINS_N,ASSETCHAINS_K>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
|
||||
// Explicit instantiations for Equihash<96,5>
|
||||
template int Equihash<48,5>::InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
template bool Equihash<48,5>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<48,5>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<48,5>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
|
||||
// Explicit instantiations for Equihash<48,5>
|
||||
template int Equihash<210,9>::InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
template bool Equihash<210,9>::BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
template bool Equihash<210,9>::OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
template bool Equihash<210,9>::IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#include "utilstrencodings.h"
|
||||
|
||||
#include "sodium.h"
|
||||
#include "komodo_nk.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
@@ -24,6 +25,9 @@ typedef crypto_generichash_blake2b_state eh_HashState;
|
||||
typedef uint32_t eh_index;
|
||||
typedef uint8_t eh_trunc;
|
||||
|
||||
#define BLAKE2B_OUTBYTES 64
|
||||
extern uint64_t ASSETCHAINS_NK[2];
|
||||
|
||||
void ExpandArray(const unsigned char* in, size_t in_len,
|
||||
unsigned char* out, size_t out_len,
|
||||
size_t bit_len, size_t byte_pad=0);
|
||||
@@ -61,7 +65,7 @@ public:
|
||||
std::string GetHex(size_t len) { return HexStr(hash, hash+len); }
|
||||
|
||||
template<size_t W>
|
||||
friend bool HasCollision(StepRow<W>& a, StepRow<W>& b, int l);
|
||||
friend bool HasCollision(StepRow<W>& a, StepRow<W>& b, size_t l);
|
||||
};
|
||||
|
||||
class CompareSR
|
||||
@@ -77,7 +81,7 @@ public:
|
||||
};
|
||||
|
||||
template<size_t WIDTH>
|
||||
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, int l);
|
||||
bool HasCollision(StepRow<WIDTH>& a, StepRow<WIDTH>& b, size_t l);
|
||||
|
||||
template<size_t WIDTH>
|
||||
class FullStepRow : public StepRow<WIDTH>
|
||||
@@ -94,7 +98,7 @@ public:
|
||||
|
||||
FullStepRow(const FullStepRow<WIDTH>& a) : StepRow<WIDTH> {a} { }
|
||||
template<size_t W>
|
||||
FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, int trim);
|
||||
FullStepRow(const FullStepRow<W>& a, const FullStepRow<W>& b, size_t len, size_t lenIndices, size_t trim);
|
||||
FullStepRow& operator=(const FullStepRow<WIDTH>& a);
|
||||
|
||||
inline bool IndicesBefore(const FullStepRow<WIDTH>& a, size_t len, size_t lenIndices) const { return memcmp(hash+len, a.hash+len, lenIndices) < 0; }
|
||||
@@ -159,17 +163,22 @@ inline constexpr size_t equihash_solution_size(unsigned int N, unsigned int K) {
|
||||
return (1 << K)*(N/(K+1)+1)/8;
|
||||
}
|
||||
|
||||
constexpr uint8_t GetSizeInBytes(size_t N)
|
||||
{
|
||||
return static_cast<uint8_t>((N + 7) / 8);
|
||||
}
|
||||
|
||||
template<unsigned int N, unsigned int K>
|
||||
class Equihash
|
||||
{
|
||||
private:
|
||||
BOOST_STATIC_ASSERT(K < N);
|
||||
BOOST_STATIC_ASSERT(N % 8 == 0);
|
||||
//BOOST_STATIC_ASSERT(N % 8 == 0);
|
||||
BOOST_STATIC_ASSERT((N/(K+1)) + 1 < 8*sizeof(eh_index));
|
||||
|
||||
public:
|
||||
enum : size_t { IndicesPerHashOutput=512/N };
|
||||
enum : size_t { HashOutput=IndicesPerHashOutput*N/8 };
|
||||
enum : size_t { HashOutput = IndicesPerHashOutput * GetSizeInBytes(N) };
|
||||
enum : size_t { CollisionBitLength=N/(K+1) };
|
||||
enum : size_t { CollisionByteLength=(CollisionBitLength+7)/8 };
|
||||
enum : size_t { HashLength=(K+1)*CollisionByteLength };
|
||||
@@ -184,79 +193,100 @@ public:
|
||||
int InitialiseState(eh_HashState& base_state);
|
||||
#ifdef ENABLE_MINING
|
||||
bool BasicSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
bool OptimisedSolve(const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled);
|
||||
#endif
|
||||
bool IsValidSolution(const eh_HashState& base_state, std::vector<unsigned char> soln);
|
||||
};
|
||||
|
||||
#include "equihash.tcc"
|
||||
/*
|
||||
* Equihash 200,9 (KMD/Zcash)
|
||||
* Equihash 150,5 (beam)
|
||||
* Equihash 144,5 (SnowGem)
|
||||
* Equihash 96,5 (Minex)
|
||||
* Equihash 48,5 (regtest)
|
||||
* Equihash 210,9 (Aion) */
|
||||
|
||||
static Equihash<96,3> Eh96_3;
|
||||
static Equihash<200,9> Eh200_9;
|
||||
static Equihash<96,5> Eh96_5;
|
||||
static Equihash<150,5> Eh150_5;
|
||||
static Equihash<144,5> Eh144_5;
|
||||
static Equihash<ASSETCHAINS_N,ASSETCHAINS_K> Eh96_5;
|
||||
static Equihash<48,5> Eh48_5;
|
||||
static Equihash<210,9> Eh210_9;
|
||||
|
||||
#define EhInitialiseState(n, k, base_state) \
|
||||
if (n == 96 && k == 3) { \
|
||||
Eh96_3.InitialiseState(base_state); \
|
||||
} else if (n == 200 && k == 9) { \
|
||||
Eh200_9.InitialiseState(base_state); \
|
||||
} else if (n == 96 && k == 5) { \
|
||||
if (n == 200 && k == 9) { \
|
||||
Eh200_9.InitialiseState(base_state); \
|
||||
} else if (n == 150 && k == 5) { \
|
||||
Eh150_5.InitialiseState(base_state); \
|
||||
} else if (n == 144 && k == 5) { \
|
||||
Eh144_5.InitialiseState(base_state); \
|
||||
} else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) { \
|
||||
Eh96_5.InitialiseState(base_state); \
|
||||
} else if (n == 48 && k == 5) { \
|
||||
Eh48_5.InitialiseState(base_state); \
|
||||
} else if (n == 210 && k == 9) { \
|
||||
Eh210_9.InitialiseState(base_state); \
|
||||
} else { \
|
||||
throw std::invalid_argument("Unsupported Equihash parameters"); \
|
||||
}
|
||||
|
||||
#ifdef ENABLE_MINING
|
||||
inline bool EhBasicSolve(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled)
|
||||
{
|
||||
if (n == 96 && k == 3) {
|
||||
return Eh96_3.BasicSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 200 && k == 9) {
|
||||
if (n == 200 && k == 9) {
|
||||
return Eh200_9.BasicSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 96 && k == 5) {
|
||||
} else if (n == 150 && k == 5) {
|
||||
return Eh150_5.BasicSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 144 && k == 5) {
|
||||
return Eh144_5.BasicSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) {
|
||||
return Eh96_5.BasicSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 48 && k == 5) {
|
||||
} else if (n == 48 && k == 5) {
|
||||
return Eh48_5.BasicSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 210 && k == 9) {
|
||||
return Eh210_9.BasicSolve(base_state, validBlock, cancelled);
|
||||
} else {
|
||||
throw std::invalid_argument("Unsupported Equihash parameters");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool EhBasicSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock)
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock)
|
||||
{
|
||||
return EhBasicSolve(n, k, base_state, validBlock,
|
||||
[](EhSolverCancelCheck pos) { return false; });
|
||||
}
|
||||
|
||||
inline bool EhOptimisedSolve(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock,
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock,
|
||||
const std::function<bool(EhSolverCancelCheck)> cancelled)
|
||||
{
|
||||
if (n == 96 && k == 3) {
|
||||
return Eh96_3.OptimisedSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 200 && k == 9) {
|
||||
if (n == 200 && k == 9) {
|
||||
return Eh200_9.OptimisedSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 96 && k == 5) {
|
||||
} else if (n == 150 && k == 5) {
|
||||
return Eh150_5.OptimisedSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 144 && k == 5) {
|
||||
return Eh144_5.OptimisedSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) {
|
||||
return Eh96_5.OptimisedSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 48 && k == 5) {
|
||||
} else if (n == 48 && k == 5) {
|
||||
return Eh48_5.OptimisedSolve(base_state, validBlock, cancelled);
|
||||
} else if (n == 210 && k == 9) {
|
||||
return Eh210_9.OptimisedSolve(base_state, validBlock, cancelled);
|
||||
} else {
|
||||
throw std::invalid_argument("Unsupported Equihash parameters");
|
||||
}
|
||||
}
|
||||
|
||||
inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const eh_HashState& base_state,
|
||||
const std::function<bool(std::vector<unsigned char>)> validBlock)
|
||||
const std::function<bool(const std::vector<unsigned char>&)> validBlock)
|
||||
{
|
||||
return EhOptimisedSolve(n, k, base_state, validBlock,
|
||||
[](EhSolverCancelCheck pos) { return false; });
|
||||
@@ -264,14 +294,18 @@ inline bool EhOptimisedSolveUncancellable(unsigned int n, unsigned int k, const
|
||||
#endif // ENABLE_MINING
|
||||
|
||||
#define EhIsValidSolution(n, k, base_state, soln, ret) \
|
||||
if (n == 96 && k == 3) { \
|
||||
ret = Eh96_3.IsValidSolution(base_state, soln); \
|
||||
} else if (n == 200 && k == 9) { \
|
||||
ret = Eh200_9.IsValidSolution(base_state, soln); \
|
||||
} else if (n == 96 && k == 5) { \
|
||||
if (n == 200 && k == 9) { \
|
||||
ret = Eh200_9.IsValidSolution(base_state, soln); \
|
||||
} else if (n == 150 && k == 5) { \
|
||||
ret = Eh150_5.IsValidSolution(base_state, soln); \
|
||||
} else if (n == 144 && k == 5) { \
|
||||
ret = Eh144_5.IsValidSolution(base_state, soln); \
|
||||
} else if (n == ASSETCHAINS_N && k == ASSETCHAINS_K) { \
|
||||
ret = Eh96_5.IsValidSolution(base_state, soln); \
|
||||
} else if (n == 48 && k == 5) { \
|
||||
ret = Eh48_5.IsValidSolution(base_state, soln); \
|
||||
} else if (n == 210 && k == 9) { \
|
||||
ret = Eh210_9.IsValidSolution(base_state, soln); \
|
||||
} else { \
|
||||
throw std::invalid_argument("Unsupported Equihash parameters"); \
|
||||
}
|
||||
|
||||
@@ -24,28 +24,63 @@
|
||||
#include "script/sign.h"
|
||||
#include "wallet/wallet.h"
|
||||
|
||||
#include "cc/CCinclude.h"
|
||||
|
||||
int32_t komodo_nextheight();
|
||||
|
||||
CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride)
|
||||
// makes import tx for either coins or tokens
|
||||
CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride)
|
||||
{
|
||||
std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN);
|
||||
//std::vector<uint8_t> payload = E_MARSHAL(ss << EVAL_IMPORTCOIN);
|
||||
CScript scriptSig;
|
||||
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
if (mtx.fOverwintered)
|
||||
mtx.nExpiryHeight = 0;
|
||||
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload));
|
||||
mtx.vout = payouts;
|
||||
auto importData = E_MARSHAL(ss << proof; ss << burnTx);
|
||||
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << importData));
|
||||
if (nExpiryHeightOverride != 0)
|
||||
mtx.nExpiryHeight = nExpiryHeightOverride; //this is for construction of the tx used for validating importtx
|
||||
if (mtx.vout.size() == 0)
|
||||
return CTransaction(mtx);
|
||||
|
||||
// add special import tx vin:
|
||||
scriptSig << E_MARSHAL(ss << EVAL_IMPORTCOIN); // simple payload for coins
|
||||
mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), scriptSig));
|
||||
|
||||
if (nExpiryHeightOverride != 0)
|
||||
mtx.nExpiryHeight = nExpiryHeightOverride; //this is for validation code, to make a tx used for validating the import tx
|
||||
|
||||
auto importData = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx); // added evalcode to differentiate importdata from token opret
|
||||
// if it is tokens:
|
||||
vscript_t vopret;
|
||||
GetOpReturnData(mtx.vout.back().scriptPubKey, vopret);
|
||||
|
||||
if (!vopret.empty()) {
|
||||
std::vector<uint8_t> vorigpubkey;
|
||||
uint8_t funcId;
|
||||
std::vector <std::pair<uint8_t, vscript_t>> oprets;
|
||||
std::string name, desc;
|
||||
|
||||
if (DecodeTokenCreateOpRet(mtx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 'c') { // parse token 'c' opret
|
||||
mtx.vout.pop_back(); //remove old token opret
|
||||
oprets.push_back(std::make_pair(OPRETID_IMPORTDATA, importData));
|
||||
mtx.vout.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make new token 'c' opret with importData
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "MakeImportCoinTransaction() incorrect token import opret" << std::endl);
|
||||
}
|
||||
}
|
||||
else { //no opret in coin payouts
|
||||
mtx.vout.push_back(CTxOut(0, CScript() << OP_RETURN << importData)); // import tx's opret now is in the vout's tail
|
||||
}
|
||||
|
||||
return CTransaction(mtx);
|
||||
}
|
||||
|
||||
|
||||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof)
|
||||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string targetSymbol, const std::vector<CTxOut> payouts, const std::vector<uint8_t> rawproof)
|
||||
{
|
||||
std::vector<uint8_t> opret;
|
||||
opret = E_MARSHAL(ss << VARINT(targetCCid);
|
||||
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN; // should mark burn opret to differentiate it from token opret
|
||||
ss << VARINT(targetCCid);
|
||||
ss << targetSymbol;
|
||||
ss << SerializeHash(payouts);
|
||||
ss << rawproof);
|
||||
@@ -53,20 +88,23 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb
|
||||
}
|
||||
|
||||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof,
|
||||
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256> txids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub)
|
||||
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256> txids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount)
|
||||
{
|
||||
std::vector<uint8_t> opret;
|
||||
opret = E_MARSHAL(ss << VARINT(targetCCid);
|
||||
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN;
|
||||
ss << VARINT(targetCCid);
|
||||
ss << targetSymbol;
|
||||
ss << SerializeHash(payouts);
|
||||
ss << rawproof;
|
||||
ss << bindtxid;
|
||||
ss << publishers;
|
||||
ss << txids;
|
||||
ss << burntxid;
|
||||
ss << height;
|
||||
ss << burnvout;
|
||||
ss << rawburntx;
|
||||
ss << destpub);
|
||||
ss << destpub;
|
||||
ss << amount);
|
||||
|
||||
return CTxOut(value, CScript() << OP_RETURN << opret);
|
||||
}
|
||||
@@ -75,7 +113,8 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb
|
||||
std::string receipt)
|
||||
{
|
||||
std::vector<uint8_t> opret;
|
||||
opret = E_MARSHAL(ss << VARINT(targetCCid);
|
||||
opret = E_MARSHAL(ss << (uint8_t)EVAL_IMPORTCOIN;
|
||||
ss << VARINT(targetCCid);
|
||||
ss << targetSymbol;
|
||||
ss << SerializeHash(payouts);
|
||||
ss << rawproof;
|
||||
@@ -85,39 +124,114 @@ CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymb
|
||||
}
|
||||
|
||||
|
||||
bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx,
|
||||
std::vector<CTxOut> &payouts)
|
||||
bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx, std::vector<CTxOut> &payouts)
|
||||
{
|
||||
std::vector<uint8_t> vData;
|
||||
GetOpReturnData(importTx.vout[importTx.vout.size()-1].scriptPubKey, vData);
|
||||
if (importTx.vout.size() < 1) return false;
|
||||
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end()-1);
|
||||
return importTx.vin.size() == 1 &&
|
||||
importTx.vin[0].scriptSig == (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN)) &&
|
||||
E_UNMARSHAL(vData, ss >> proof; ss >> burnTx);
|
||||
if (importTx.vout.size() < 1)
|
||||
return false;
|
||||
|
||||
if (importTx.vin.size() != 1 || importTx.vin[0].scriptSig != (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN))) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() incorrect import tx vin" << std::endl);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<uint8_t> vImportData;
|
||||
GetOpReturnData(importTx.vout.back().scriptPubKey, vImportData);
|
||||
if (vImportData.empty()) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() no opret" << std::endl);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vImportData.begin()[0] == EVAL_TOKENS) { // if it is tokens
|
||||
// get import data after token opret:
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
std::vector<uint8_t> vorigpubkey;
|
||||
std::string name, desc;
|
||||
|
||||
if (DecodeTokenCreateOpRet(importTx.vout.back().scriptPubKey, vorigpubkey, name, desc, oprets) == 0) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() could not decode token opret" << std::endl);
|
||||
return false;
|
||||
}
|
||||
|
||||
GetOpretBlob(oprets, OPRETID_IMPORTDATA, vImportData); // fetch import data after token opret
|
||||
for (std::vector<std::pair<uint8_t, vscript_t>>::const_iterator i = oprets.begin(); i != oprets.end(); i++)
|
||||
if ((*i).first == OPRETID_IMPORTDATA) {
|
||||
oprets.erase(i); // remove import data from token opret to restore original payouts:
|
||||
break;
|
||||
}
|
||||
|
||||
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end()-1); //exclude opret with import data
|
||||
payouts.push_back(CTxOut(0, EncodeTokenCreateOpRet('c', vorigpubkey, name, desc, oprets))); // make original payouts token opret (without import data)
|
||||
}
|
||||
else {
|
||||
//payouts = std::vector<CTxOut>(importTx.vout.begin()+1, importTx.vout.end()); // see next
|
||||
payouts = std::vector<CTxOut>(importTx.vout.begin(), importTx.vout.end() - 1); // skip opret; and it is now in the back
|
||||
}
|
||||
|
||||
uint8_t evalCode;
|
||||
bool retcode = E_UNMARSHAL(vImportData, ss >> evalCode; ss >> proof; ss >> burnTx);
|
||||
if (!retcode)
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalImportTx() could not unmarshal import data" << std::endl);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector<uint8_t>&rawproof)
|
||||
{
|
||||
std::vector<uint8_t> burnOpret; uint32_t ccid = 0; bool isEof=true;
|
||||
std::vector<uint8_t> vburnOpret; uint32_t ccid = 0;
|
||||
uint8_t evalCode;
|
||||
|
||||
if (burnTx.vout.size() == 0) return false;
|
||||
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret);
|
||||
return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid);
|
||||
ss >> targetSymbol;
|
||||
ss >> payoutsHash;
|
||||
ss >> rawproof; isEof=ss.eof();) || !isEof;
|
||||
if (burnTx.vout.size() == 0)
|
||||
return false;
|
||||
|
||||
GetOpReturnData(burnTx.vout.back().scriptPubKey, vburnOpret);
|
||||
if (vburnOpret.empty()) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() cannot unmarshal burn tx: empty burn opret" << std::endl);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint256 tokenid;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
|
||||
if (DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) != 't')
|
||||
return false;
|
||||
|
||||
//skip token opret:
|
||||
GetOpretBlob(oprets, OPRETID_BURNDATA, vburnOpret); // fetch burnOpret after token opret
|
||||
if (vburnOpret.empty()) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() cannot unmarshal token burn tx: empty burn opret for tokenid=" << tokenid.GetHex() << std::endl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (vburnOpret.begin()[0] == EVAL_IMPORTCOIN) {
|
||||
uint8_t evalCode;
|
||||
bool isEof = true;
|
||||
return E_UNMARSHAL(vburnOpret, ss >> evalCode;
|
||||
ss >> VARINT(*targetCCid);
|
||||
ss >> targetSymbol;
|
||||
ss >> payoutsHash;
|
||||
ss >> rawproof; isEof = ss.eof();) || !isEof; // if isEof == false it means we have successfully read the vars upto 'rawproof'
|
||||
// and it might be additional data further that we do not need here so we allow !isEof
|
||||
}
|
||||
else {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "UnmarshalBurnTx() invalid eval code in opret" << std::endl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt)
|
||||
{
|
||||
std::vector<uint8_t> burnOpret,rawproof; bool isEof=true;
|
||||
std::string targetSymbol; uint32_t targetCCid; uint256 payoutsHash;
|
||||
uint8_t evalCode;
|
||||
|
||||
if (burnTx.vout.size() == 0) return false;
|
||||
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret);
|
||||
return (E_UNMARSHAL(burnOpret, ss >> VARINT(targetCCid);
|
||||
return (E_UNMARSHAL(burnOpret, ss >> evalCode;
|
||||
ss >> VARINT(targetCCid);
|
||||
ss >> targetSymbol;
|
||||
ss >> payoutsHash;
|
||||
ss >> rawproof;
|
||||
@@ -125,24 +239,28 @@ bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::strin
|
||||
ss >> receipt));
|
||||
}
|
||||
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector<CPubKey> &publishers,std::vector<uint256> &txids,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub)
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector<CPubKey> &publishers,std::vector<uint256> &txids,uint256& burntxid,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub, int64_t &amount)
|
||||
{
|
||||
std::vector<uint8_t> burnOpret,rawproof; bool isEof=true;
|
||||
uint32_t targetCCid; uint256 payoutsHash; std::string targetSymbol;
|
||||
uint8_t evalCode;
|
||||
|
||||
if (burnTx.vout.size() == 0) return false;
|
||||
GetOpReturnData(burnTx.vout.back().scriptPubKey, burnOpret);
|
||||
return (E_UNMARSHAL(burnOpret, ss >> VARINT(targetCCid);
|
||||
return (E_UNMARSHAL(burnOpret, ss >> evalCode;
|
||||
ss >> VARINT(targetCCid);
|
||||
ss >> targetSymbol;
|
||||
ss >> payoutsHash;
|
||||
ss >> rawproof;
|
||||
ss >> bindtxid;
|
||||
ss >> publishers;
|
||||
ss >> txids;
|
||||
ss >> burntxid;
|
||||
ss >> height;
|
||||
ss >> burnvout;
|
||||
ss >> rawburntx;
|
||||
ss >> destpub));
|
||||
ss >> destpub;
|
||||
ss >> amount));
|
||||
}
|
||||
|
||||
|
||||
@@ -151,16 +269,55 @@ bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector<CPu
|
||||
*/
|
||||
CAmount GetCoinImportValue(const CTransaction &tx)
|
||||
{
|
||||
TxProof proof;
|
||||
ImportProof proof;
|
||||
CTransaction burnTx;
|
||||
std::vector<CTxOut> payouts;
|
||||
if (UnmarshalImportTx(tx, proof, burnTx, payouts)) {
|
||||
return burnTx.vout.size() ? burnTx.vout.back().nValue : 0;
|
||||
|
||||
bool isNewImportTx = false;
|
||||
if ((isNewImportTx = UnmarshalImportTx(tx, proof, burnTx, payouts))) {
|
||||
if (burnTx.vout.size() > 0) {
|
||||
vscript_t vburnOpret;
|
||||
|
||||
GetOpReturnData(burnTx.vout.back().scriptPubKey, vburnOpret);
|
||||
if (vburnOpret.empty()) {
|
||||
LOGSTREAM("importcoin", CCLOG_INFO, stream << "GetCoinImportValue() empty burn opret" << std::endl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (isNewImportTx && vburnOpret.begin()[0] == EVAL_TOKENS) { //if it is tokens
|
||||
|
||||
uint8_t evalCodeInOpret;
|
||||
uint256 tokenid;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
if (DecodeTokenOpRet(tx.vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) == 0)
|
||||
return 0;
|
||||
|
||||
uint8_t nonfungibleEvalCode = EVAL_TOKENS; // init as if no non-fungibles
|
||||
vscript_t vnonfungibleOpret;
|
||||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vnonfungibleOpret);
|
||||
if (!vnonfungibleOpret.empty())
|
||||
nonfungibleEvalCode = vnonfungibleOpret.begin()[0];
|
||||
|
||||
// calc outputs for burn tx
|
||||
int64_t ccBurnOutputs = 0;
|
||||
for (auto v : burnTx.vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(nonfungibleEvalCode, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey
|
||||
ccBurnOutputs += v.nValue;
|
||||
|
||||
return ccBurnOutputs + burnTx.vout.back().nValue; // total token burned value
|
||||
}
|
||||
else
|
||||
return burnTx.vout.back().nValue; // coin burned value
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* CoinImport is different enough from normal script execution that it's not worth
|
||||
* making all the mods neccesary in the interpreter to do the dispatch correctly.
|
||||
|
||||
@@ -22,22 +22,90 @@
|
||||
#include "script/interpreter.h"
|
||||
#include <cryptoconditions.h>
|
||||
|
||||
enum ProofKind : uint8_t {
|
||||
PROOF_NONE = 0x00,
|
||||
PROOF_MERKLEBRANCH = 0x11,
|
||||
PROOF_NOTARYTXIDS = 0x12,
|
||||
PROOF_MERKLEBLOCK = 0x13
|
||||
};
|
||||
|
||||
class ImportProof {
|
||||
|
||||
private:
|
||||
uint8_t proofKind;
|
||||
TxProof proofBranch;
|
||||
std::vector<uint256> notaryTxids;
|
||||
std::vector<uint8_t> proofBlock;
|
||||
|
||||
public:
|
||||
ImportProof() { proofKind = PROOF_NONE; }
|
||||
ImportProof(const TxProof &_proofBranch) {
|
||||
proofKind = PROOF_MERKLEBRANCH; proofBranch = _proofBranch;
|
||||
}
|
||||
ImportProof(const std::vector<uint256> &_notaryTxids) {
|
||||
proofKind = PROOF_NOTARYTXIDS; notaryTxids = _notaryTxids;
|
||||
}
|
||||
ImportProof(const std::vector<uint8_t> &_proofBlock) {
|
||||
proofKind = PROOF_MERKLEBLOCK; proofBlock = _proofBlock;
|
||||
}
|
||||
|
||||
ADD_SERIALIZE_METHODS
|
||||
|
||||
template <typename Stream, typename Operation>
|
||||
inline void SerializationOp(Stream& s, Operation ser_action) {
|
||||
READWRITE(proofKind);
|
||||
if (proofKind == PROOF_MERKLEBRANCH)
|
||||
READWRITE(proofBranch);
|
||||
else if (proofKind == PROOF_NOTARYTXIDS)
|
||||
READWRITE(notaryTxids);
|
||||
else if (proofKind == PROOF_MERKLEBLOCK)
|
||||
READWRITE(proofBlock);
|
||||
else
|
||||
proofKind = PROOF_NONE; // if we have read some trash
|
||||
}
|
||||
|
||||
bool IsMerkleBranch(TxProof &_proofBranch) const {
|
||||
if (proofKind == PROOF_MERKLEBRANCH) {
|
||||
_proofBranch = proofBranch;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
bool IsNotaryTxids(std::vector<uint256> &_notaryTxids) const {
|
||||
if (proofKind == PROOF_NOTARYTXIDS) {
|
||||
_notaryTxids = notaryTxids;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
bool IsMerkleBlock(std::vector<uint8_t> &_proofBlock) const {
|
||||
if (proofKind == PROOF_MERKLEBLOCK) {
|
||||
_proofBlock = proofBlock;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
CAmount GetCoinImportValue(const CTransaction &tx);
|
||||
|
||||
CTransaction MakeImportCoinTransaction(const TxProof proof,
|
||||
const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride = 0);
|
||||
CTransaction MakeImportCoinTransaction(const ImportProof proof, const CTransaction burnTx, const std::vector<CTxOut> payouts, uint32_t nExpiryHeightOverride = 0);
|
||||
|
||||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof);
|
||||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, const std::string targetSymbol, const std::vector<CTxOut> payouts, const std::vector<uint8_t> rawproof);
|
||||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof,
|
||||
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256>txids,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub);
|
||||
uint256 bindtxid,std::vector<CPubKey> publishers,std::vector<uint256>txids,uint256 burntxid,int32_t height,int32_t burnvout,std::string rawburntx,CPubKey destpub, int64_t amount);
|
||||
CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector<CTxOut> payouts,std::vector<uint8_t> rawproof,std::string srcaddr,
|
||||
std::string receipt);
|
||||
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash,std::vector<uint8_t> &rawproof);
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx, std::string &srcaddr, std::string &receipt);
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector<CPubKey> &publishers,std::vector<uint256> &txids,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub);
|
||||
bool UnmarshalImportTx(const CTransaction importTx, TxProof &proof, CTransaction &burnTx,std::vector<CTxOut> &payouts);
|
||||
bool UnmarshalBurnTx(const CTransaction burnTx,uint256 &bindtxid,std::vector<CPubKey> &publishers,std::vector<uint256> &txids,uint256& burntxid,int32_t &height,int32_t &burnvout,std::string &rawburntx,CPubKey &destpub, int64_t &amount);
|
||||
bool UnmarshalImportTx(const CTransaction importTx, ImportProof &proof, CTransaction &burnTx,std::vector<CTxOut> &payouts);
|
||||
|
||||
bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state);
|
||||
|
||||
@@ -45,4 +113,9 @@ void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, i
|
||||
void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs);
|
||||
int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs);
|
||||
|
||||
bool CheckVinPubKey(const CTransaction &sourcetx, int32_t i, uint8_t pubkey33[33]);
|
||||
|
||||
CMutableTransaction MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount);
|
||||
int32_t GetSelfimportProof(const CMutableTransaction sourceMtx, CMutableTransaction &templateMtx, ImportProof &proofNull);
|
||||
|
||||
#endif /* IMPORTCOIN_H */
|
||||
|
||||
@@ -1224,7 +1224,6 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height)
|
||||
return(0);
|
||||
|
||||
int32_t i,j,n=0,txn_count; int64_t nSubsidy; uint64_t commission,total = 0;
|
||||
txn_count = pblock->vtx.size();
|
||||
if ( ASSETCHAINS_FOUNDERS != 0 )
|
||||
{
|
||||
nSubsidy = GetBlockSubsidy(height,Params().GetConsensus());
|
||||
@@ -1242,8 +1241,9 @@ uint64_t komodo_commission(const CBlock *pblock,int32_t height)
|
||||
else commission = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
else if ( pblock != 0 )
|
||||
{
|
||||
txn_count = pblock->vtx.size();
|
||||
for (i=0; i<txn_count; i++)
|
||||
{
|
||||
n = pblock->vtx[i].vout.size();
|
||||
@@ -1855,7 +1855,7 @@ uint64_t komodo_notarypayamount(int32_t nHeight, int64_t notarycount)
|
||||
// Because of reorgs we cannot use the notarized height value.
|
||||
// We need to basically guess here and just pay some static amount.
|
||||
// Has the unwanted effect of varying coin emission, but cannot be helped.
|
||||
fprintf(stderr, "era.%i paying total of %lu\n",curEra, ASSETCHAINS_NOTARY_PAY[curEra]);
|
||||
//fprintf(stderr, "era.%i paying total of %lu\n",curEra, ASSETCHAINS_NOTARY_PAY[curEra]);
|
||||
ret = ASSETCHAINS_NOTARY_PAY[curEra] / notarycount;
|
||||
return(ret);
|
||||
}
|
||||
@@ -1870,13 +1870,8 @@ int32_t komodo_getnotarizedheight(uint32_t timestamp,int32_t height, uint8_t *sc
|
||||
if ( len >= sizeof(uint32_t) && len <= sizeof(scriptbuf) )
|
||||
{
|
||||
memcpy(scriptbuf,script,len);
|
||||
if ( komodo_voutupdate(true,&isratification,0,scriptbuf,len,height,uint256(),1,1,&voutmask,&specialtx,¬arizedheight,0,1,0,timestamp) == -2 )
|
||||
if ( komodo_voutupdate(true,&isratification,0,scriptbuf,len,height,uint256(),1,1,&voutmask,&specialtx,¬arizedheight,0,1,0,timestamp) != -2 )
|
||||
{
|
||||
fprintf(stderr, ">>>>>>VALID NOTARIZATION ht.%i\n",notarizedheight);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should no longer happen. Unless notaries are making actual invalid notarizations.
|
||||
fprintf(stderr, "<<<<<<INVALID NOTARIZATION ht.%i\n",notarizedheight);
|
||||
return(0);
|
||||
}
|
||||
@@ -2029,7 +2024,7 @@ uint64_t komodo_checknotarypay(CBlock *pblock,int32_t height)
|
||||
}
|
||||
if ( matches != 0 && matches == NotarisationNotaries.size() && totalsats == total )
|
||||
{
|
||||
fprintf(stderr, "Validated coinbase matches notarisation in tx position 1.\n" );
|
||||
//fprintf(stderr, "Validated coinbase matches notarisation in tx position 1.\n" );
|
||||
return(totalsats);
|
||||
}
|
||||
return(0);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#ifndef KOMODO_DEFS_H
|
||||
#define KOMODO_DEFS_H
|
||||
#include "komodo_nk.h"
|
||||
|
||||
#define ASSETCHAINS_MINHEIGHT 128
|
||||
#define ASSETCHAINS_MAX_ERAS 3
|
||||
@@ -53,7 +54,7 @@ extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH,ASSETCHAINS_EQUIHASH,KOM
|
||||
extern int32_t KOMODO_MININGTHREADS,KOMODO_LONGESTCHAIN,ASSETCHAINS_SEED,IS_KOMODO_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,KOMODO_ON_DEMAND,KOMODO_PASSPORT_INITDONE,ASSETCHAINS_STAKED;
|
||||
extern uint64_t ASSETCHAINS_COMMISSION, ASSETCHAINS_LASTERA,ASSETCHAINS_CBOPRET;
|
||||
extern bool VERUS_MINTBLOCKS;
|
||||
extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[];
|
||||
extern uint64_t ASSETCHAINS_REWARD[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_NOTARY_PAY[ASSETCHAINS_MAX_ERAS], ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_NONCEMASK[],ASSETCHAINS_NK[2];
|
||||
extern const char *ASSETCHAINS_ALGORITHMS[];
|
||||
extern int32_t VERUS_MIN_STAKEAGE;
|
||||
extern uint32_t ASSETCHAINS_VERUSHASH, ASSETCHAINS_VERUSHASHV1_1, ASSETCHAINS_NONCESHIFT[], ASSETCHAINS_HASHESPERROUND[];
|
||||
|
||||
@@ -2017,7 +2017,7 @@ int32_t get_stockprices(uint32_t now,uint32_t *prices,std::vector<std::string> s
|
||||
{
|
||||
char url[32768],*symbol,*timestr; cJSON *json,*obj; int32_t i,n=0,retval=-1; uint32_t uprice,timestamp;
|
||||
sprintf(url,"https://api.iextrading.com/1.0/tops/last?symbols=%s",GetArg("-ac_stocks","").c_str());
|
||||
if ( (json= send_curl(url,(char *)"iex")) != 0 ) //if ( (json= get_urljson(url)) != 0 )
|
||||
if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"iex")) != 0 ) //
|
||||
{
|
||||
if ( (n= cJSON_GetArraySize(json)) > 0 )
|
||||
{
|
||||
@@ -2056,7 +2056,7 @@ uint32_t get_dailyfx(uint32_t *prices)
|
||||
//{"base":"USD","rates":{"BGN":1.74344803,"NZD":1.471652701,"ILS":3.6329113924,"RUB":65.1997682296,"CAD":1.3430201462,"USD":1.0,"PHP":52.8641469068,"CHF":0.9970582992,"AUD":1.4129078267,"JPY":110.6792654662,"TRY":5.6523444464,"HKD":7.8499732573,"MYR":4.0824567659,"HRK":6.6232840078,"CZK":22.9862720628,"IDR":14267.4986628633,"DKK":6.6551078624,"NOK":8.6806917454,"HUF":285.131039401,"GBP":0.7626582278,"MXN":19.4183455161,"THB":31.8702085933,"ISK":122.5708682475,"ZAR":14.7033339276,"BRL":3.9750401141,"SGD":1.3573720806,"PLN":3.8286682118,"INR":69.33187734,"KRW":1139.1602781244,"RON":4.2423783206,"CNY":6.7387234801,"SEK":9.3385630237,"EUR":0.8914244963},"date":"2019-03-28"}
|
||||
char url[512],*datestr; cJSON *json,*rates; int32_t i; uint32_t datenum=0,price = 0;
|
||||
sprintf(url,"https://api.openrates.io/latest?base=USD");
|
||||
if ( (json= send_curl(url,(char *)"dailyfx")) != 0 )
|
||||
if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"dailyfx")) != 0 )
|
||||
{
|
||||
if ( (rates= jobj(json,(char *)"rates")) != 0 )
|
||||
{
|
||||
@@ -2079,7 +2079,7 @@ uint32_t get_binanceprice(const char *symbol)
|
||||
{
|
||||
char url[512]; cJSON *json; uint32_t price = 0;
|
||||
sprintf(url,"https://api.binance.com/api/v1/ticker/price?symbol=%sBTC",symbol);
|
||||
if ( (json= send_curl(url,(char *)"bnbprice")) != 0 )
|
||||
if ( (json= get_urljson(url)) != 0 ) //if ( (json= send_curl(url,(char *)"bnbprice")) != 0 )
|
||||
{
|
||||
price = jdouble(json,(char *)"price")*SATOSHIDEN + 0.0000000049;
|
||||
free_json(json);
|
||||
|
||||
@@ -44,7 +44,7 @@ struct komodo_state KOMODO_STATES[34];
|
||||
#define _COINBASE_MATURITY 100
|
||||
int COINBASE_MATURITY = _COINBASE_MATURITY;//100;
|
||||
unsigned int WITNESS_CACHE_SIZE = _COINBASE_MATURITY+10;
|
||||
|
||||
uint256 KOMODO_EARLYTXID;
|
||||
int32_t KOMODO_MININGTHREADS = -1,IS_KOMODO_NOTARY,IS_STAKED_NOTARY,USE_EXTERNAL_PUBKEY,KOMODO_CHOSEN_ONE,ASSETCHAINS_SEED,KOMODO_ON_DEMAND,KOMODO_EXTERNAL_NOTARIES,KOMODO_PASSPORT_INITDONE,KOMODO_PAX,KOMODO_EXCHANGEWALLET,KOMODO_REWIND,STAKED_ERA,KOMODO_CONNECTING = -1,KOMODO_DEALERNODE,KOMODO_EXTRASATOSHI,ASSETCHAINS_FOUNDERS;
|
||||
int32_t KOMODO_INSYNC,KOMODO_LASTMINED,prevKOMODO_LASTMINED,KOMODO_CCACTIVATE,JUMBLR_PAUSE = 1;
|
||||
std::string NOTARY_PUBKEY,ASSETCHAINS_NOTARIES,ASSETCHAINS_OVERRIDE_PUBKEY,DONATION_PUBKEY,ASSETCHAINS_SCRIPTPUB,NOTARY_ADDRESS,ASSETCHAINS_SELFIMPORT,ASSETCHAINS_CCLIB;
|
||||
|
||||
7
src/komodo_nk.h
Normal file
7
src/komodo_nk.h
Normal file
@@ -0,0 +1,7 @@
|
||||
#ifndef KOMODO_NK_H
|
||||
#define KOMODO_NK_H
|
||||
|
||||
#define ASSETCHAINS_N 96
|
||||
#define ASSETCHAINS_K 5
|
||||
|
||||
#endif
|
||||
@@ -1667,6 +1667,28 @@ extern int64_t MAX_MONEY;
|
||||
void komodo_cbopretupdate(int32_t forceflag);
|
||||
void SplitStr(const std::string& strVal, std::vector<std::string> &outVals);
|
||||
|
||||
int8_t equihash_params_possible(uint64_t n, uint64_t k)
|
||||
{
|
||||
/* To add more of these you also need to edit:
|
||||
* equihash.cpp very end of file with the tempate to point to the new param numbers
|
||||
* equihash.h
|
||||
* line 210/217 (declaration of equihash class)
|
||||
* Add this object to the following functions:
|
||||
* EhInitialiseState
|
||||
* EhBasicSolve
|
||||
* EhOptimisedSolve
|
||||
* EhIsValidSolution
|
||||
* Alternatively change ASSETCHAINS_N and ASSETCHAINS_K in komodo_nk.h for fast testing.
|
||||
*/
|
||||
if ( k == 9 && (n == 200 || n == 210) )
|
||||
return(0);
|
||||
if ( k == 5 && (n == 150 || n == 144 || n == 96 || n == 48) )
|
||||
return(0);
|
||||
if ( k == ASSETCHAINS_K && n == ASSETCHAINS_N)
|
||||
return(0);
|
||||
return(-1);
|
||||
}
|
||||
|
||||
void komodo_args(char *argv0)
|
||||
{
|
||||
extern const char *Notaries_elected1[][2];
|
||||
@@ -1729,10 +1751,12 @@ void komodo_args(char *argv0)
|
||||
ASSETCHAINS_BLOCKTIME = GetArg("-ac_blocktime",60);
|
||||
ASSETCHAINS_PUBLIC = GetArg("-ac_public",0);
|
||||
ASSETCHAINS_PRIVATE = GetArg("-ac_private",0);
|
||||
Split(GetArg("-ac_nk",""), ASSETCHAINS_NK, 0);
|
||||
if ( (KOMODO_REWIND= GetArg("-rewind",0)) != 0 )
|
||||
{
|
||||
printf("KOMODO_REWIND %d\n",KOMODO_REWIND);
|
||||
}
|
||||
KOMODO_EARLYTXID = Parseuint256(GetArg("-earlytxid","0").c_str());
|
||||
if ( name.c_str()[0] != 0 )
|
||||
{
|
||||
std::string selectedAlgo = GetArg("-ac_algo", std::string(ASSETCHAINS_ALGORITHMS[0]));
|
||||
@@ -1749,6 +1773,14 @@ void komodo_args(char *argv0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( ASSETCHAINS_ALGO == ASSETCHAINS_EQUIHASH && ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0 )
|
||||
{
|
||||
if ( equihash_params_possible(ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]) == -1 )
|
||||
{
|
||||
printf("equihash values N.%li and K.%li are not currently available\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
|
||||
exit(0);
|
||||
} else printf("ASSETCHAINS_ALGO, algorithm set to equihash with N.%li and K.%li\n", ASSETCHAINS_NK[0], ASSETCHAINS_NK[1]);
|
||||
}
|
||||
if (i == ASSETCHAINS_NUMALGOS)
|
||||
{
|
||||
printf("ASSETCHAINS_ALGO, %s not supported. using equihash\n", selectedAlgo.c_str());
|
||||
@@ -1901,7 +1933,12 @@ void komodo_args(char *argv0)
|
||||
StartShutdown();
|
||||
}
|
||||
// else it can be gateway coin
|
||||
|
||||
else if (!ASSETCHAINS_SELFIMPORT.empty() && (ASSETCHAINS_ENDSUBSIDY[0]!=1 || ASSETCHAINS_SUPPLY>10 || ASSETCHAINS_COMMISSION!=0))
|
||||
{
|
||||
fprintf(stderr,"when using gateway import these must be set: -ac_end=1 -ac_supply=0 -ac_perc=0\n");
|
||||
StartShutdown();
|
||||
}
|
||||
|
||||
|
||||
if ( (ASSETCHAINS_STAKED= GetArg("-ac_staked",0)) > 100 )
|
||||
ASSETCHAINS_STAKED = 100;
|
||||
@@ -1974,7 +2011,7 @@ void komodo_args(char *argv0)
|
||||
fprintf(stderr,"-ac_script and -ac_marmara are mutually exclusive\n");
|
||||
StartShutdown();
|
||||
}
|
||||
if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 )
|
||||
if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 || (ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0) )
|
||||
{
|
||||
fprintf(stderr,"perc %.4f%% ac_pub=[%02x%02x%02x...] acsize.%d\n",dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0],ASSETCHAINS_OVERRIDE_PUBKEY33[1],ASSETCHAINS_OVERRIDE_PUBKEY33[2],(int32_t)ASSETCHAINS_SCRIPTPUB.size());
|
||||
extraptr = extrabuf;
|
||||
@@ -2104,6 +2141,11 @@ void komodo_args(char *argv0)
|
||||
komodo_cbopretupdate(1); // will set Mineropret
|
||||
fprintf(stderr,"This blockchain uses data produced from CoinDesk Bitcoin Price Index\n");
|
||||
}
|
||||
if ( ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0 )
|
||||
{
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_NK[0]),(void *)&ASSETCHAINS_NK[0]);
|
||||
extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_NK[1]),(void *)&ASSETCHAINS_NK[1]);
|
||||
}
|
||||
}
|
||||
|
||||
addn = GetArg("-seednode","");
|
||||
|
||||
@@ -3308,7 +3308,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin
|
||||
REJECT_INVALID, "bad-cb-amount");
|
||||
// calculate the notaries compensation and validate the amounts and pubkeys are correct.
|
||||
uint64_t notarypaycheque = komodo_checknotarypay((CBlock *)&block,(int32_t)pindex->GetHeight());
|
||||
fprintf(stderr, "notarypaycheque.%lu\n", notarypaycheque);
|
||||
//fprintf(stderr, "notarypaycheque.%lu\n", notarypaycheque);
|
||||
if ( notarypaycheque > 0 )
|
||||
blockReward += notarypaycheque;
|
||||
else
|
||||
|
||||
@@ -371,7 +371,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
|
||||
}
|
||||
dPriority += (double)nValueIn * nConf;
|
||||
}
|
||||
if ( numSN != 0 && TMP_NotarisationNotaries.size() >= numSN / 5 )
|
||||
if ( numSN != 0 && notarypubkeys[0][0] != 0 && TMP_NotarisationNotaries.size() >= numSN / 5 )
|
||||
{
|
||||
// check a notary didnt sign twice (this would be an invalid notarisation later on and cause problems)
|
||||
std::set<int> checkdupes( TMP_NotarisationNotaries.begin(), TMP_NotarisationNotaries.end() );
|
||||
@@ -418,7 +418,7 @@ CBlockTemplate* CreateNewBlock(CPubKey _pk,const CScript& _scriptPubKeyIn, int32
|
||||
NotarisationNotaries = TMP_NotarisationNotaries;
|
||||
dPriority = 1e16;
|
||||
fNotarisationBlock = true;
|
||||
fprintf(stderr, "Notarisation %s set to maximum priority\n",hash.ToString().c_str());
|
||||
//fprintf(stderr, "Notarisation %s set to maximum priority\n",hash.ToString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1608,9 +1608,10 @@ void static BitcoinMiner()
|
||||
if ( notaryid != My_notaryid )
|
||||
My_notaryid = notaryid;
|
||||
std::string solver;
|
||||
//if ( notaryid >= 0 || ASSETCHAINS_SYMBOL[0] != 0 )
|
||||
solver = "tromp";
|
||||
//else solver = "default";
|
||||
if ( ASSETCHAINS_NK[0] == 0 && ASSETCHAINS_NK[1] == 0 )
|
||||
solver = "tromp";
|
||||
else
|
||||
solver = "default";
|
||||
assert(solver == "tromp" || solver == "default");
|
||||
LogPrint("pow", "Using Equihash solver \"%s\" with n = %u, k = %u\n", solver, n, k);
|
||||
if ( ASSETCHAINS_SYMBOL[0] != 0 )
|
||||
@@ -1951,11 +1952,11 @@ void static BitcoinMiner()
|
||||
ehSolverRuns.increment();
|
||||
if (found) {
|
||||
int32_t i; uint256 hash = pblock->GetHash();
|
||||
for (i=0; i<32; i++)
|
||||
fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
|
||||
fprintf(stderr," <- %s Block found %d\n",ASSETCHAINS_SYMBOL,Mining_height);
|
||||
FOUND_BLOCK = 1;
|
||||
KOMODO_MAYBEMINED = Mining_height;
|
||||
//for (i=0; i<32; i++)
|
||||
// fprintf(stderr,"%02x",((uint8_t *)&hash)[i]);
|
||||
//fprintf(stderr," <- %s Block found %d\n",ASSETCHAINS_SYMBOL,Mining_height);
|
||||
//FOUND_BLOCK = 1;
|
||||
//KOMODO_MAYBEMINED = Mining_height;
|
||||
break;
|
||||
}
|
||||
} catch (EhSolverCancelledException&) {
|
||||
@@ -1968,12 +1969,12 @@ void static BitcoinMiner()
|
||||
// Check for stop or if block needs to be rebuilt
|
||||
boost::this_thread::interruption_point();
|
||||
// Regtest mode doesn't require peers
|
||||
if ( FOUND_BLOCK != 0 )
|
||||
/*if ( FOUND_BLOCK != 0 )
|
||||
{
|
||||
FOUND_BLOCK = 0;
|
||||
fprintf(stderr,"FOUND_BLOCK!\n");
|
||||
//sleep(2000);
|
||||
}
|
||||
} */
|
||||
if (vNodes.empty() && chainparams.MiningRequiresPeers())
|
||||
{
|
||||
if ( ASSETCHAINS_SYMBOL[0] == 0 || Mining_height > ASSETCHAINS_MINHEIGHT )
|
||||
|
||||
@@ -23,8 +23,8 @@ static const char *iguanaSeeds[8][1] =
|
||||
static const int STAKED_ERA_GAP = 777;
|
||||
|
||||
static const int NUM_STAKED_ERAS = 4;
|
||||
static const int STAKED_NOTARIES_TIMESTAMP[NUM_STAKED_ERAS] = {1542964044, 1549188000, 1604233333, 1604244444};
|
||||
static const int32_t num_notaries_STAKED[NUM_STAKED_ERAS] = { 17, 25, 19, 17 };
|
||||
static const int STAKED_NOTARIES_TIMESTAMP[NUM_STAKED_ERAS] = {1604244444, 1604244444, 1604244444, 1604244444};
|
||||
static const int32_t num_notaries_STAKED[NUM_STAKED_ERAS] = { 20, 25, 19, 17 };
|
||||
|
||||
// Era array of pubkeys.
|
||||
static const char *notaries_STAKED[NUM_STAKED_ERAS][64][2] =
|
||||
@@ -33,20 +33,23 @@ static const char *notaries_STAKED[NUM_STAKED_ERAS][64][2] =
|
||||
{"blackjok3r", "021914947402d936a89fbdd1b12be49eb894a1568e5e17bb18c8a6cffbd3dc106e" }, // RTVti13NP4eeeZaCCmQxc2bnPdHxCJFP9x
|
||||
{"alright", "0285657c689b903218c97f5f10fe1d10ace2ed6595112d9017f54fb42ea1c1dda8" }, //RXmXeQ8LfJK6Y1aTM97cRz9Gu5f6fmR3sg
|
||||
{"webworker01", "031d1fb39ae4dca28965c3abdbd21faa0f685f6d7b87a60561afa7c448343fef6d" }, //RGsQiArk5sTmjXZV9UzGMW5njyvtSnsTN8
|
||||
{"CrisF", "03f87f1bccb744d90fdbf7fad1515a98e9fc7feb1800e460d2e7565b88c3971bf3" }, //RMwEpnaVe3cesWbMqqKYPPkaLcDkooTDgZ
|
||||
{"smk762", "02eacef682d2f86e0103c18f4da46116e17196f3fb8f73ed931acb78e81d8e1aa5" }, // RQVvzJ8gepCDVjhqCAc5Tia1kTmt8KDPL9
|
||||
{"jorian", "02150c410a606b898bcab4f083e48e0f98a510e0d48d4db367d37f318d26ae72e3" }, // RFgzxZe2P4RWKx6E9QGPK3rx3TXeWxSqa8
|
||||
{"CrisF", "024d19acf0d5de212cdd50326cd143292545d366a71b2b9c6df9f2110de2dfa1f2" }, // RKtAD2kyRRMx4EiG1eeTNprF5h2nmGbzzu
|
||||
{"smk762", "029f6c1f38c4d6825acb3b4b5147f7992e943b617cdaa0f4f5f36187e239d52d5a" }, // RPy6Xj2LWrxNoEW9YyREDgBZDZZ5qURXBU
|
||||
{"jorian", "0288e682c1ac449f1b85c4acb2d0bcd216d5df34c15fd18b8a8dd5fa64b8ece8ef" }, // RR1yT5aB19VwFoUCGTW4q4pk4qmhHEEE4t
|
||||
{"TonyL", "021a559101e355c907d9c553671044d619769a6e71d624f68bfec7d0afa6bd6a96" }, // RHq3JsvLxU45Z8ufYS6RsDpSG4wi6ucDev
|
||||
{"Emman", "038f642dcdacbdf510b7869d74544dbc6792548d9d1f8d73a999dd9f45f513c935" }, //RN2KsQGW36Ah4NorJDxLJp2xiYJJEzk9Y6
|
||||
{"CHMEX", "03ed125d1beb118d12ff0a052bdb0cee32591386d718309b2924f2c36b4e7388e6" }, // RF4HiVeuYpaznRPs7fkRAKKYqT5tuxQQTL
|
||||
{"metaphilibert", "0344182c376f054e3755d712361672138660bda8005abb64067eb5aa98bdb40d10" }, // RG28QSnYFADBg1dAVkH1uPGYS6F8ioEUM2
|
||||
{"greentea", "02054c14ae81838a063d22a75eaa3c961415f6825a57c8b8e4148d19dad64f128e" }, // REF7R76WpL1v7nSXjjiNHtRa2xYtq5qk1p
|
||||
{"CMaurice", "025830ce81bd1301fb67d5872344efa7a9ff99ae85fe1234f18c085db9072b740f" }, // RX7pXUaV24xFn6DVKV8t3PrRF3gKw6TBjf
|
||||
{"Bar_F1sh_Rel", "0395f2d9dd9ccb78caf74bff49b6d959afb95af746462e1b35f4a167d8e82b3666" }, // RBbLxJagCA9QHDazQvfnDZe874V1K4Gu8t
|
||||
{"jusoaresf", "02dfb7ed72a23f6d07f0ea2f28192ee174733cc8412ec0f97b073007b78fab6346" }, // RBQGfE5Hxsjm1BPraTxbneRuNasPDuoLnu
|
||||
{"zatJUM", "030fff499b6dc0215344b28a0b6b4becdfb00cd34cd1b36b983ec14f47965fd4bc" }, // RSoEDLBasth7anxS8gbkg6KgeGiz8rhqv1
|
||||
{"dwy", "03669457b2934d98b5761121dd01b243aed336479625b293be9f8c43a6ae7aaeff" }, // RKhZMqRF361FSGFAzstP5AhozekPjoVh5q
|
||||
{"gcharang", "03336ca9db27cb6e882830e20dc525884e27dc94d557a5e68b972a5cbf9e8c62a8" }, // RJYiWn3FRCSSLf9Pe5RJcbrKQYosaMburP
|
||||
{"computergenie", "027313dabde94fb72f823231d0a1c59fc7baa2e5b3bb2af97ca7d70aae116026b9" }, // RLabsCGxTRqJcJvz6foKuXAB61puJ2x8yt
|
||||
{"daemonfox", "0383484bdc745b2b953c85b5a0c496a1f27bc42ae971f15779ed1532421b3dd943" }, //
|
||||
{"SHossain", "02791f5c215b8a19c143a98e3371ff03b5613df9ac430c4a331ca55fed5761c800" }, // RKdLoHkyeorXmMtj91B1AAnAGiwsdt9MdF
|
||||
{"Nabob", "03ee91c20b6d26e3604022f42df6bb8de6f669da4591f93b568778cba13d9e9ddf" }, // RRwCLPZDzpHEFJnLev4phy51e2stHRUAaU
|
||||
{"mylo", "03f6b7fcaf0b8b8ec432d0de839a76598b78418dadd50c8e5594c0e557d914ec09" }, // RXN4hoZkhUkkrnef9nTUDw3E3vVALAD8Kx
|
||||
{"blackjok3r2", "02f7597468703c1c5c8465dd6d43acaae697df9df30bed21494d193412a1ea193e" }, // RWHGbrLSP89fTzNVF9U9xiekDYJqcibTca
|
||||
{"blackjok3r3", "03c3e4c0206551dbf3a4b24d18e5d2737080541184211e3bfd2b1092177410b9c2" }, // RMMav2AVse5XHPvDfTzRpMbFhK3GqFmtSN
|
||||
{"kmdkrazy", "02f7597468703c1c5c8465dd6d43acaae697df9df30bed21494d193412a1ea193e" }, // RWHGbrLSP89fTzNVF9U9xiekDYJqcibTca
|
||||
{"alrighttest", "02e9dfe248f453b499315a90375e58a1c9ad79f5f3932ecb2205399a0f262d65fc" }, // RBevSstS8JtDXMEFNcJws4QTYN4PcE2VL5
|
||||
{"alrighttest1", "03527c7ecd6a8c5db6d685a64e6e18c1edb49e2f057a434f56c3f1253a26e9c6a2" }, // RBw2jNU3dnGk86ZLqPMadJwRwg3NU8eC6s
|
||||
},
|
||||
{
|
||||
{"blackjok3r", "021914947402d936a89fbdd1b12be49eb894a1568e5e17bb18c8a6cffbd3dc106e" }, // RTVti13NP4eeeZaCCmQxc2bnPdHxCJFP9x
|
||||
|
||||
@@ -344,6 +344,9 @@ bool CheckEquihashSolution(const CBlockHeader *pblock, const CChainParams& param
|
||||
{
|
||||
if (ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH)
|
||||
return true;
|
||||
|
||||
if ( ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0 && pblock->GetHash().ToString() == "027e3758c3a65b12aa1046462b486d0a63bfa1beae327897f56c5cfb7daaae71" )
|
||||
return true;
|
||||
|
||||
unsigned int n = params.EquihashN();
|
||||
unsigned int k = params.EquihashK();
|
||||
|
||||
@@ -108,6 +108,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "lockunspent", 0 },
|
||||
{ "lockunspent", 1 },
|
||||
{ "importprivkey", 2 },
|
||||
{ "importprivkey", 3 },
|
||||
{ "importaddress", 2 },
|
||||
{ "verifychain", 0 },
|
||||
{ "verifychain", 1 },
|
||||
@@ -174,6 +175,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
||||
{ "getNotarisationsForBlock", 0},
|
||||
{ "height_MoM", 1},
|
||||
{ "calc_MoM", 2},
|
||||
{ "migrate_completeimporttransaction", 1},
|
||||
};
|
||||
|
||||
class CRPCConvertTable
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
|
||||
#include "key_io.h"
|
||||
#include "cc/CCImportGateway.h"
|
||||
#include "cc/CCtokens.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <univalue.h>
|
||||
@@ -52,6 +53,8 @@ extern std::string CCerror;
|
||||
extern std::string ASSETCHAINS_SELFIMPORT;
|
||||
extern uint16_t ASSETCHAINS_CODAPORT, ASSETCHAINS_BEAMPORT;
|
||||
int32_t ensure_CCrequirements(uint8_t evalcode);
|
||||
bool EnsureWalletIsAvailable(bool avoidException);
|
||||
|
||||
|
||||
int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip);
|
||||
int32_t komodo_MoMoMdata(char *hexstr,int32_t hexsize,struct komodo_ccdataMoMoM *mdata,char *symbol,int32_t kmdheight,int32_t notarized_height);
|
||||
@@ -60,8 +63,8 @@ uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth);
|
||||
int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
|
||||
extern std::string ASSETCHAINS_SELFIMPORT;
|
||||
|
||||
std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx);
|
||||
int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount);
|
||||
//std::string MakeSelfImportSourceTx(CTxDestination &dest, int64_t amount, CMutableTransaction &mtx);
|
||||
//int32_t GetSelfimportProof(std::string source, CMutableTransaction &mtx, CScript &scriptPubKey, TxProof &proof, std::string rawsourcetx, int32_t &ivout, uint256 sourcetxid, uint64_t burnAmount);
|
||||
std::string MakeCodaImportTx(uint64_t txfee, std::string receipt, std::string srcaddr, std::vector<CTxOut> vouts);
|
||||
|
||||
UniValue assetchainproof(const UniValue& params, bool fHelp)
|
||||
@@ -137,12 +140,12 @@ UniValue MoMoMdata(const UniValue& params, bool fHelp)
|
||||
int kmdheight = atoi(params[1].get_str().c_str());
|
||||
uint32_t ccid = atoi(params[2].get_str().c_str());
|
||||
ret.push_back(Pair("coin",symbol));
|
||||
ret.push_back(Pair("kmdheight",kmdheight));
|
||||
ret.push_back(Pair("kmdheight",kmdheight-5));
|
||||
ret.push_back(Pair("ccid", (int) ccid));
|
||||
|
||||
uint256 destNotarisationTxid;
|
||||
std::vector<uint256> moms;
|
||||
uint256 MoMoM = CalculateProofRoot(symbol, ccid, kmdheight, moms, destNotarisationTxid);
|
||||
uint256 MoMoM = CalculateProofRoot(symbol, ccid, kmdheight-5, moms, destNotarisationTxid);
|
||||
|
||||
UniValue valMoms(UniValue::VARR);
|
||||
for (int i=0; i<moms.size(); i++) valMoms.push_back(moms[i].GetHex());
|
||||
@@ -185,7 +188,7 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
|
||||
"If neccesary, the transaction should be funded using fundrawtransaction.\n"
|
||||
"Finally, the transaction should be signed using signrawtransaction\n"
|
||||
"The finished export transaction, plus the payouts, should be passed to "
|
||||
"the \"migrate_createimporttransaction\" method on a KMD node to get the corresponding "
|
||||
"the \"migrate_createimporttransaction\" method to get the corresponding "
|
||||
"import transaction.\n"
|
||||
);
|
||||
|
||||
@@ -206,19 +209,30 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
|
||||
|
||||
if (strcmp(ASSETCHAINS_SYMBOL,targetSymbol.c_str()) == 0)
|
||||
throw runtime_error("cant send a coin to the same chain");
|
||||
|
||||
/// Tested 44 vins p2pkh inputs as working. Set this at 25, but its a tx size limit.
|
||||
// likely with a single RPC you can limit it by the size of tx.
|
||||
if (tx.vout.size() > 25)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot have more than 50 vins, transaction too large.");
|
||||
|
||||
CAmount burnAmount = 0;
|
||||
|
||||
for (int i=0; i<tx.vout.size(); i++) burnAmount += tx.vout[i].nValue;
|
||||
if (burnAmount <= 0)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export a negative or zero value.");
|
||||
// This is due to MAX MONEY in target. We set it at min 1 million coins, so you cant export more than 1 million,
|
||||
// without knowing the MAX money on the target this was the easiest solution.
|
||||
if (burnAmount > 1000000LL*COIN)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export more than 1 million coins per export.");
|
||||
|
||||
/* note: we marshal to rawproof in a different way (to be able to add other objects)
|
||||
rawproof.resize(strlen(ASSETCHAINS_SYMBOL));
|
||||
ptr = rawproof.data();
|
||||
for (i=0; i<rawproof.size(); i++)
|
||||
ptr[i] = ASSETCHAINS_SYMBOL[i];
|
||||
ptr[i] = ASSETCHAINS_SYMBOL[i]; */
|
||||
const std::string chainSymbol(ASSETCHAINS_SYMBOL);
|
||||
rawproof = E_MARSHAL(ss << chainSymbol); // add src chain name
|
||||
|
||||
CTxOut burnOut = MakeBurnOutput(burnAmount+txfee, ccid, targetSymbol, tx.vout,rawproof);
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << tx.vout))));
|
||||
@@ -228,26 +242,308 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp)
|
||||
return ret;
|
||||
}
|
||||
|
||||
// creates burn tx as an alternative to 'migrate_converttoexport()'
|
||||
UniValue migrate_createburntransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
//uint8_t *ptr;
|
||||
//uint8_t i;
|
||||
uint32_t ccid = ASSETCHAINS_CC;
|
||||
int64_t txfee = 10000;
|
||||
|
||||
if (fHelp || params.size() != 3 && params.size() != 4)
|
||||
throw runtime_error(
|
||||
"migrate_createburntransaction dest_symbol dest_addr amount [tokenid]\n"
|
||||
"\nCreates a raw burn transaction to make a cross-chain coin or non-fungible token transfer.\n"
|
||||
"The parameters:\n"
|
||||
"dest_symbol destination chain ac_name\n"
|
||||
"dest_addr address on the destination chain where coins are to be sent or pubkey if tokens are to be sent\n"
|
||||
"amount amount in coins to be burned on the source chain and sent to the destination address/pubkey on the destination chain, for tokens should be equal to 1\n"
|
||||
"tokenid token id, if tokens are transferred (optional). Only non-fungible tokens are supported\n"
|
||||
"\n"
|
||||
"The transaction should be sent using sendrawtransaction to the source chain\n"
|
||||
"The finished burn transaction and payouts should be also passed to "
|
||||
"the \"migrate_createimporttransaction\" method to get the corresponding import transaction.\n"
|
||||
);
|
||||
|
||||
if (ASSETCHAINS_CC < KOMODO_FIRSTFUNGIBLEID)
|
||||
throw runtime_error("-ac_cc < KOMODO_FIRSTFUNGIBLEID");
|
||||
|
||||
if (ASSETCHAINS_SYMBOL[0] == 0)
|
||||
throw runtime_error("Must be called on assetchain");
|
||||
|
||||
// if -pubkey not set it sends change to null pubkey.
|
||||
// we need a better way to return errors from this function!
|
||||
if (ensure_CCrequirements(225) < 0)
|
||||
throw runtime_error("You need to set -pubkey, or run setpukbey RPC, or imports are disabled on this chain.");
|
||||
|
||||
string targetSymbol = params[0].get_str();
|
||||
if (targetSymbol.size() == 0 || targetSymbol.size() > 32)
|
||||
throw runtime_error("targetSymbol length must be >0 and <=32");
|
||||
|
||||
if (strcmp(ASSETCHAINS_SYMBOL, targetSymbol.c_str()) == 0)
|
||||
throw runtime_error("cant send a coin to the same chain");
|
||||
|
||||
std::string dest_addr_or_pubkey = params[1].get_str();
|
||||
|
||||
CAmount burnAmount;
|
||||
if(params.size() == 3)
|
||||
burnAmount = (CAmount)( atof(params[2].get_str().c_str()) * COIN + 0.00000000499999 );
|
||||
else
|
||||
burnAmount = atoll(params[2].get_str().c_str());
|
||||
|
||||
if (burnAmount <= 0)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export a negative or zero value.");
|
||||
if (burnAmount > 1000000LL * COIN)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Cannot export more than 1 million coins per export.");
|
||||
|
||||
uint256 tokenid = zeroid;
|
||||
if( params.size() == 4 )
|
||||
tokenid = Parseuint256(params[3].get_str().c_str());
|
||||
|
||||
CPubKey myPubKey = Mypubkey();
|
||||
struct CCcontract_info *cpTokens, C;
|
||||
cpTokens = CCinit(&C, EVAL_TOKENS);
|
||||
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
|
||||
const std::string chainSymbol(ASSETCHAINS_SYMBOL);
|
||||
std::vector<uint8_t> rawproof; //(chainSymbol.begin(), chainSymbol.end());
|
||||
|
||||
if (tokenid.IsNull()) { // coins
|
||||
int64_t inputs;
|
||||
if ((inputs = AddNormalinputs(mtx, myPubKey, burnAmount + txfee, 60)) == 0) {
|
||||
throw runtime_error("Cannot find normal inputs\n");
|
||||
}
|
||||
|
||||
CTxDestination txdest = DecodeDestination(dest_addr_or_pubkey.c_str());
|
||||
CScript scriptPubKey = GetScriptForDestination(txdest);
|
||||
if (!scriptPubKey.IsPayToPublicKeyHash()) {
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "Incorrect destination addr.");
|
||||
}
|
||||
mtx.vout.push_back(CTxOut(burnAmount, scriptPubKey)); // 'model' vout
|
||||
ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << mtx.vout)))); // save 'model' vout
|
||||
|
||||
rawproof = E_MARSHAL(ss << chainSymbol); // add src chain name
|
||||
|
||||
CTxOut burnOut = MakeBurnOutput(burnAmount+txfee, ccid, targetSymbol, mtx.vout, rawproof); //make opret with burned amount
|
||||
|
||||
mtx.vout.clear(); // remove 'model' vout
|
||||
|
||||
int64_t change = inputs - (burnAmount+txfee);
|
||||
if (change != 0)
|
||||
mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG)); // make change here to prevent it from making in FinalizeCCtx
|
||||
|
||||
mtx.vout.push_back(burnOut); // mtx now has only burned vout (that is, amount sent to OP_RETURN making it unspendable)
|
||||
//std::string exportTxHex = FinalizeCCTx(0, cpTokens, mtx, myPubKey, txfee, CScript()); // no change no opret
|
||||
|
||||
}
|
||||
else { // tokens
|
||||
CTransaction tokenbasetx;
|
||||
uint256 hashBlock;
|
||||
vscript_t vopretNonfungible;
|
||||
vscript_t vopretBurnData;
|
||||
std::vector<uint8_t> vorigpubkey, vdestpubkey;
|
||||
std::string name, description;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
if (!myGetTransaction(tokenid, tokenbasetx, hashBlock))
|
||||
throw runtime_error("Could not load token creation tx\n");
|
||||
|
||||
// check if it is non-fungible tx and get its second evalcode from non-fungible payload
|
||||
if (tokenbasetx.vout.size() == 0)
|
||||
throw runtime_error("No vouts in token tx\n");
|
||||
|
||||
if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) != 'c')
|
||||
throw runtime_error("Incorrect token creation tx\n");
|
||||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
|
||||
/* allow fungible tokens:
|
||||
if (vopretNonfungible.empty())
|
||||
throw runtime_error("No non-fungible token data\n"); */
|
||||
|
||||
uint8_t destEvalCode = EVAL_TOKENS;
|
||||
if (!vopretNonfungible.empty())
|
||||
destEvalCode = vopretNonfungible.begin()[0];
|
||||
|
||||
// check non-fungible tokens amount
|
||||
if (!vopretNonfungible.empty() && burnAmount != 1)
|
||||
throw JSONRPCError(RPC_TYPE_ERROR, "For non-fungible tokens amount should be equal to 1.");
|
||||
|
||||
vdestpubkey = ParseHex(dest_addr_or_pubkey);
|
||||
CPubKey destPubKey = pubkey2pk(vdestpubkey);
|
||||
if (!destPubKey.IsValid())
|
||||
throw runtime_error("Invalid destination pubkey\n");
|
||||
|
||||
int64_t inputs;
|
||||
if ((inputs = AddNormalinputs(mtx, myPubKey, txfee, 1)) == 0) // for miners in dest chain
|
||||
throw runtime_error("No normal input found for two txfee\n");
|
||||
|
||||
int64_t ccInputs;
|
||||
if ((ccInputs = AddTokenCCInputs(cpTokens, mtx, myPubKey, tokenid, burnAmount, 4)) < burnAmount)
|
||||
throw runtime_error("No token inputs found (please try to consolidate tokens)\n");
|
||||
|
||||
// make payouts (which will be in the import tx with token):
|
||||
mtx.vout.push_back(MakeCC1vout(EVAL_TOKENS, txfee, GetUnspendable(cpTokens, NULL))); // new marker to token cc addr, burnable and validated, vout position now changed to 0 (from 1)
|
||||
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, burnAmount, destPubKey));
|
||||
|
||||
std::vector<std::pair<uint8_t, vscript_t>> voprets;
|
||||
if (!vopretNonfungible.empty())
|
||||
voprets.push_back(std::make_pair(OPRETID_NONFUNGIBLEDATA, vopretNonfungible)); // add additional opret with non-fungible data
|
||||
|
||||
mtx.vout.push_back(CTxOut((CAmount)0, EncodeTokenCreateOpRet('c', vorigpubkey, name, description, voprets))); // make token import opret
|
||||
ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << mtx.vout)))); // save payouts for import tx
|
||||
|
||||
rawproof = E_MARSHAL(ss << chainSymbol << tokenbasetx); // add src chain name and token creation tx
|
||||
|
||||
CTxOut burnOut = MakeBurnOutput(0, ccid, targetSymbol, mtx.vout, rawproof); //make opret with amount=0 because tokens are burned, not coins (see next vout)
|
||||
mtx.vout.clear(); // remove payouts
|
||||
|
||||
// now make burn transaction:
|
||||
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, burnAmount, pubkey2pk(ParseHex(CC_BURNPUBKEY)))); // burn tokens
|
||||
|
||||
int64_t change = inputs - txfee;
|
||||
if (change != 0)
|
||||
mtx.vout.push_back(CTxOut(change, CScript() << ParseHex(HexStr(myPubKey)) << OP_CHECKSIG)); // make change here to prevent it from making in FinalizeCCtx
|
||||
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
voutTokenPubkeys.push_back(pubkey2pk(ParseHex(CC_BURNPUBKEY))); // maybe we do not need this because ccTokens has the const for burn pubkey
|
||||
|
||||
int64_t ccChange = ccInputs - burnAmount;
|
||||
if (ccChange != 0)
|
||||
mtx.vout.push_back(MakeTokensCC1vout(destEvalCode, ccChange, myPubKey));
|
||||
|
||||
GetOpReturnData(burnOut.scriptPubKey, vopretBurnData);
|
||||
mtx.vout.push_back(CTxOut(txfee, EncodeTokenOpRet(tokenid, voutTokenPubkeys, std::make_pair(OPRETID_BURNDATA, vopretBurnData)))); //burn txfee for miners in dest chain
|
||||
}
|
||||
|
||||
std::string burnTxHex = FinalizeCCTx(0, cpTokens, mtx, myPubKey, txfee, CScript()); //no change, no opret
|
||||
ret.push_back(Pair("BurnTxHex", burnTxHex));
|
||||
return ret;
|
||||
}
|
||||
|
||||
// util func to check burn tx and source chain params
|
||||
void CheckBurnTxSource(uint256 burntxid, UniValue &info) {
|
||||
|
||||
CTransaction burnTx;
|
||||
uint256 blockHash;
|
||||
|
||||
if (!GetTransaction(burntxid, burnTx, blockHash, true))
|
||||
throw std::runtime_error("Cannot find burn transaction");
|
||||
|
||||
if (blockHash.IsNull())
|
||||
throw std::runtime_error("Burn tx still in mempool");
|
||||
|
||||
uint256 payoutsHash;
|
||||
std::string targetSymbol;
|
||||
uint32_t targetCCid;
|
||||
std::vector<uint8_t> rawproof;
|
||||
|
||||
if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash, rawproof))
|
||||
throw std::runtime_error("Cannot unmarshal burn tx data");
|
||||
|
||||
vscript_t vopret;
|
||||
std::string sourceSymbol;
|
||||
CTransaction tokenbasetxStored;
|
||||
uint256 tokenid = zeroid;
|
||||
|
||||
if (burnTx.vout.size() > 0 && GetOpReturnData(burnTx.vout.back().scriptPubKey, vopret) && !vopret.empty()) {
|
||||
if (vopret.begin()[0] == EVAL_TOKENS) {
|
||||
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol; ss >> tokenbasetxStored))
|
||||
throw std::runtime_error("Cannot unmarshal rawproof for tokens");
|
||||
|
||||
uint8_t evalCode;
|
||||
std::vector<CPubKey> voutPubkeys;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
if( DecodeTokenOpRet(burnTx.vout.back().scriptPubKey, evalCode, tokenid, voutPubkeys, oprets) == 0 )
|
||||
throw std::runtime_error("Cannot decode token opret in burn tx");
|
||||
|
||||
if( tokenid != tokenbasetxStored.GetHash() )
|
||||
throw std::runtime_error("Incorrect tokenbase in burn tx");
|
||||
|
||||
CTransaction tokenbasetx;
|
||||
uint256 hashBlock;
|
||||
if (!myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
|
||||
throw std::runtime_error("Could not load tokenbase tx");
|
||||
}
|
||||
|
||||
// check if nonfungible data present
|
||||
if (tokenbasetx.vout.size() > 0) {
|
||||
std::vector<uint8_t> origpubkey;
|
||||
std::string name, description;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
vscript_t vopretNonfungible;
|
||||
if (DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, origpubkey, name, description, oprets) == 'c') {
|
||||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
|
||||
if (vopretNonfungible.empty())
|
||||
throw std::runtime_error("Could not migrate fungible tokens");
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Could not decode opreturn in tokenbase tx");
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Incorrect tokenbase tx: not opreturn");
|
||||
|
||||
|
||||
struct CCcontract_info *cpTokens, CCtokens_info;
|
||||
cpTokens = CCinit(&CCtokens_info, EVAL_TOKENS);
|
||||
int64_t ccInputs = 0, ccOutputs = 0;
|
||||
if( !TokensExactAmounts(true, cpTokens, ccInputs, ccOutputs, NULL, burnTx, tokenid) )
|
||||
throw std::runtime_error("Incorrect token burn tx: cc inputs <> cc outputs");
|
||||
}
|
||||
else if (vopret.begin()[0] == EVAL_IMPORTCOIN) {
|
||||
if (!E_UNMARSHAL(rawproof, ss >> sourceSymbol))
|
||||
throw std::runtime_error("Cannot unmarshal rawproof for coins");
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("Incorrect eval code in opreturn");
|
||||
}
|
||||
else
|
||||
throw std::runtime_error("No opreturn in burn tx");
|
||||
|
||||
|
||||
if (sourceSymbol != ASSETCHAINS_SYMBOL)
|
||||
throw std::runtime_error("Incorrect source chain in rawproof");
|
||||
|
||||
if (targetCCid != ASSETCHAINS_CC)
|
||||
throw std::runtime_error("Incorrect CCid in burn tx");
|
||||
|
||||
if (targetSymbol == ASSETCHAINS_SYMBOL)
|
||||
throw std::runtime_error("Must not be called on the destination chain");
|
||||
|
||||
// fill info to return for the notary operator (if manual notarization) or user
|
||||
info.push_back(Pair("SourceSymbol", sourceSymbol));
|
||||
info.push_back(Pair("TargetSymbol", targetSymbol));
|
||||
info.push_back(Pair("TargetCCid", std::to_string(targetCCid)));
|
||||
if (!tokenid.IsNull())
|
||||
info.push_back(Pair("tokenid", tokenid.GetHex()));
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* The process to migrate funds
|
||||
* The process to migrate funds from a chain to chain
|
||||
*
|
||||
* Create a transaction on assetchain:
|
||||
* 1.Create a transaction on assetchain (deprecated):
|
||||
* 1.1 generaterawtransaction
|
||||
* 1.2 migrate_converttoexport
|
||||
* 1.3 fundrawtransaction
|
||||
* 1.4 signrawtransaction
|
||||
*
|
||||
* generaterawtransaction
|
||||
* migrate_converttoexport
|
||||
* fundrawtransaction
|
||||
* signrawtransaction
|
||||
* alternatively, burn (export) transaction may be created with this new rpc call:
|
||||
* 1. migrate_createburntransaction
|
||||
*
|
||||
* migrate_createimportransaction
|
||||
* migrate_completeimporttransaction
|
||||
* next steps:
|
||||
* 2. migrate_createimporttransaction
|
||||
* 3. migrate_completeimporttransaction
|
||||
*/
|
||||
|
||||
UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 2)
|
||||
throw runtime_error("migrate_createimporttransaction burnTx payouts\n\n"
|
||||
"Create an importTx given a burnTx and the corresponding payouts, hex encoded");
|
||||
if (fHelp || params.size() < 2)
|
||||
throw runtime_error("migrate_createimporttransaction burnTx payouts [notarytxid-1]..[notarytxid-N]\n\n"
|
||||
"Create an importTx given a burnTx and the corresponding payouts, hex encoded\n"
|
||||
"optional notarytxids are txids of notary operator proofs of burn tx existense (from destination chain).\n"
|
||||
"Do not make subsequent call to migrate_completeimporttransaction if notary txids are set");
|
||||
|
||||
if (ASSETCHAINS_CC < KOMODO_FIRSTFUNGIBLEID)
|
||||
throw runtime_error("-ac_cc < KOMODO_FIRSTFUNGIBLEID");
|
||||
@@ -261,26 +557,52 @@ UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp)
|
||||
if (!E_UNMARSHAL(txData, ss >> burnTx))
|
||||
throw runtime_error("Couldn't parse burnTx");
|
||||
|
||||
if( burnTx.vin.size() == 0 )
|
||||
throw runtime_error("No vins in the burnTx");
|
||||
|
||||
if (burnTx.vout.size() == 0)
|
||||
throw runtime_error("No vouts in the burnTx");
|
||||
|
||||
|
||||
vector<CTxOut> payouts;
|
||||
if (!E_UNMARSHAL(ParseHexV(params[1], "argument 2"), ss >> payouts))
|
||||
throw runtime_error("Couldn't parse payouts");
|
||||
|
||||
uint256 txid = burnTx.GetHash();
|
||||
TxProof proof = GetAssetchainProof(burnTx.GetHash(),burnTx);
|
||||
ImportProof importProof;
|
||||
if (params.size() == 2) { // standard MoMoM based notarization
|
||||
// get MoM import proof
|
||||
importProof = ImportProof(GetAssetchainProof(burnTx.GetHash(), burnTx));
|
||||
}
|
||||
else { // notarization by manual operators notary tx
|
||||
UniValue info(UniValue::VOBJ);
|
||||
CheckBurnTxSource(burnTx.GetHash(), info);
|
||||
|
||||
CTransaction importTx = MakeImportCoinTransaction(proof, burnTx, payouts);
|
||||
// get notary import proof
|
||||
std::vector<uint256> notaryTxids;
|
||||
for (int i = 2; i < params.size(); i++) {
|
||||
uint256 txid = Parseuint256(params[i].get_str().c_str());
|
||||
if (txid.IsNull())
|
||||
throw runtime_error("Incorrect notary approval txid");
|
||||
notaryTxids.push_back(txid);
|
||||
}
|
||||
importProof = ImportProof(notaryTxids);
|
||||
}
|
||||
|
||||
return HexStr(E_MARSHAL(ss << importTx));
|
||||
CTransaction importTx = MakeImportCoinTransaction(importProof, burnTx, payouts);
|
||||
|
||||
std::string importTxHex = HexStr(E_MARSHAL(ss << importTx));
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.push_back(Pair("ImportTxHex", importTxHex));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error("migrate_completeimporttransaction importTx\n\n"
|
||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||
throw runtime_error("migrate_completeimporttransaction importTx [offset]\n\n"
|
||||
"Takes a cross chain import tx with proof generated on assetchain "
|
||||
"and extends proof to target chain proof root");
|
||||
"and extends proof to target chain proof root\n"
|
||||
"offset is optional, use it to increase the used KMD height, use when import fails.");
|
||||
|
||||
if (ASSETCHAINS_SYMBOL[0] != 0)
|
||||
throw runtime_error("Must be called on KMD");
|
||||
@@ -288,63 +610,130 @@ UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp)
|
||||
CTransaction importTx;
|
||||
if (!E_UNMARSHAL(ParseHexV(params[0], "argument 1"), ss >> importTx))
|
||||
throw runtime_error("Couldn't parse importTx");
|
||||
|
||||
int32_t offset = 0;
|
||||
if ( params.size() == 2 )
|
||||
offset = params[1].get_int();
|
||||
|
||||
CompleteImportTransaction(importTx);
|
||||
CompleteImportTransaction(importTx, offset);
|
||||
|
||||
return HexStr(E_MARSHAL(ss << importTx));
|
||||
std::string importTxHex = HexStr(E_MARSHAL(ss << importTx));
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.push_back(Pair("ImportTxHex", importTxHex));
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Alternate coin migration solution if MoMoM migration has failed
|
||||
*
|
||||
* The workflow:
|
||||
* On the source chain user calls migrate_createburntransaction, sends the burn tx to the chain and sends its txid and the source chain name to the notary operators (off-chain)
|
||||
* the notary operators call migrate_checkburntransactionsource on the source chain
|
||||
* on the destination chain the notary operators call migrate_createnotaryapprovaltransaction and pass the burn txid and txoutproof received from the previous call,
|
||||
* the notary operators send the approval transactions to the chain and send their txids to the user (off-chain)
|
||||
* on the source chain the user calls migrate_createimporttransaction and passes to it notary txids as additional parameters
|
||||
* then the user sends the import transaction to the destination chain (where the notary approvals will be validated)
|
||||
*/
|
||||
|
||||
// checks if burn tx exists and params stored in the burn tx match to the source chain
|
||||
// returns txproof
|
||||
// run it on the source chain
|
||||
UniValue migrate_checkburntransactionsource(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 1)
|
||||
throw runtime_error("migrate_checkburntransactionsource burntxid\n\n"
|
||||
"checks if params stored in the burn tx match to its tx chain");
|
||||
|
||||
if (ASSETCHAINS_SYMBOL[0] == 0)
|
||||
throw runtime_error("Must be called on asset chain");
|
||||
|
||||
uint256 burntxid = Parseuint256(params[0].get_str().c_str());
|
||||
UniValue result(UniValue::VOBJ);
|
||||
CheckBurnTxSource(burntxid, result); // check and get burn tx data
|
||||
|
||||
// get tx proof for burn tx
|
||||
UniValue nextparams(UniValue::VARR);
|
||||
UniValue txids(UniValue::VARR);
|
||||
txids.push_back(burntxid.GetHex());
|
||||
nextparams.push_back(txids);
|
||||
result.push_back(Pair("TxOutProof", gettxoutproof(nextparams, false))); // get txoutproof
|
||||
result.push_back(Pair("result", "success")); // get txoutproof
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// creates a tx for the dest chain with txproof
|
||||
// used as a momom-backup manual import solution
|
||||
// run it on the dest chain
|
||||
UniValue migrate_createnotaryapprovaltransaction(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() != 2)
|
||||
throw runtime_error("migrate_createnotaryapprovaltransaction burntxid txoutproof\n\n"
|
||||
"Creates a tx for destination chain with burn tx proof\n"
|
||||
"txoutproof should be retrieved by komodo-cli migrate_checkburntransactionsource call on the source chain\n" );
|
||||
|
||||
if (ASSETCHAINS_SYMBOL[0] == 0)
|
||||
throw runtime_error("Must be called on asset chain");
|
||||
|
||||
uint256 burntxid = Parseuint256(params[0].get_str().c_str());
|
||||
if (burntxid.IsNull())
|
||||
throw runtime_error("Couldn't parse burntxid or it is null");
|
||||
|
||||
std::vector<uint8_t> proofData = ParseHex(params[1].get_str());
|
||||
CMerkleBlock merkleBlock;
|
||||
std::vector<uint256> prooftxids;
|
||||
if (!E_UNMARSHAL(proofData, ss >> merkleBlock))
|
||||
throw runtime_error("Couldn't parse txoutproof");
|
||||
|
||||
merkleBlock.txn.ExtractMatches(prooftxids);
|
||||
if (std::find(prooftxids.begin(), prooftxids.end(), burntxid) == prooftxids.end())
|
||||
throw runtime_error("No burntxid in txoutproof");
|
||||
|
||||
const int64_t txfee = 10000;
|
||||
struct CCcontract_info *cpDummy, C;
|
||||
cpDummy = CCinit(&C, EVAL_TOKENS); // just for FinalizeCCtx to work
|
||||
|
||||
// creating a tx with proof:
|
||||
CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight());
|
||||
if (AddNormalinputs(mtx, Mypubkey(), txfee*2, 4) == 0)
|
||||
throw runtime_error("Cannot find normal inputs\n");
|
||||
|
||||
mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(Mypubkey())) << OP_CHECKSIG));
|
||||
std::string notaryTxHex = FinalizeCCTx(0, cpDummy, mtx, Mypubkey(), txfee, CScript() << OP_RETURN << E_MARSHAL(ss << proofData;));
|
||||
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("NotaryTxHex", notaryTxHex));
|
||||
return result;
|
||||
}
|
||||
|
||||
// creates a source 'quasi-burn' tx for AC_PUBKEY
|
||||
// run it on the same asset chain
|
||||
UniValue selfimport(const UniValue& params, bool fHelp)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ);
|
||||
CMutableTransaction sourceMtx, templateMtx;
|
||||
std::string destaddr;
|
||||
std::string source;
|
||||
std::string rawsourcetx;
|
||||
std::string sourceTxHex;
|
||||
std::string importTxHex;
|
||||
CTransaction burnTx;
|
||||
CTxOut burnOut;
|
||||
uint64_t burnAmount;
|
||||
uint256 sourcetxid, blockHash;
|
||||
std::vector<CTxOut> vouts;
|
||||
std::vector<uint8_t> rawproof, rawproofEmpty;
|
||||
int32_t ivout = 0;
|
||||
CScript scriptPubKey;
|
||||
TxProof proof;
|
||||
std::vector<uint8_t> rawproof;
|
||||
|
||||
if ( ASSETCHAINS_SELFIMPORT.size() == 0 )
|
||||
throw runtime_error("selfimport only works on -ac_import chains");
|
||||
|
||||
if (fHelp || params.size() != 2)
|
||||
throw runtime_error("selfimport destaddr amount\n"
|
||||
//old: "selfimport rawsourcetx sourcetxid {nvout|\"find\"} amount \n"
|
||||
//TODO: "or selfimport rawburntx burntxid {nvout|\"find\"} rawproof source bindtxid height} \n"
|
||||
"\ncreates self import coin transaction");
|
||||
|
||||
/* OLD selfimport schema:
|
||||
rawsourcetx = params[0].get_str();
|
||||
sourcetxid = Parseuint256((char *)params[1].get_str().c_str()); // allow for txid != hash(rawtx)
|
||||
|
||||
int32_t ivout = -1;
|
||||
if( params[2].get_str() != "find" ) {
|
||||
if( !std::all_of(params[2].get_str().begin(), params[2].get_str().end(), ::isdigit) ) // check if not all chars are digit
|
||||
throw std::runtime_error("incorrect nvout param");
|
||||
|
||||
ivout = atoi(params[2].get_str().c_str());
|
||||
}
|
||||
|
||||
burnAmount = atof(params[3].get_str().c_str()) * COIN + 0.00000000499999; */
|
||||
|
||||
destaddr = params[0].get_str();
|
||||
burnAmount = atof(params[1].get_str().c_str()) * COIN + 0.00000000499999;
|
||||
|
||||
source = ASSETCHAINS_SELFIMPORT; //defaults to -ac_import=... param
|
||||
/* TODO for gateways:
|
||||
if ( params.size() >= 5 )
|
||||
{
|
||||
rawproof = ParseHex(params[4].get_str().c_str());
|
||||
if ( params.size() == 6 )
|
||||
source = params[5].get_str();
|
||||
} */
|
||||
|
||||
if (source == "BEAM")
|
||||
{
|
||||
@@ -364,51 +753,34 @@ UniValue selfimport(const UniValue& params, bool fHelp)
|
||||
}
|
||||
else if (source == "PUBKEY")
|
||||
{
|
||||
|
||||
ImportProof proofNull;
|
||||
CTxDestination dest = DecodeDestination(destaddr.c_str());
|
||||
rawsourcetx = MakeSelfImportSourceTx(dest, burnAmount, sourceMtx);
|
||||
sourcetxid = sourceMtx.GetHash();
|
||||
|
||||
CMutableTransaction sourceMtx = MakeSelfImportSourceTx(dest, burnAmount); // make self-import source tx
|
||||
vscript_t rawProofEmpty;
|
||||
|
||||
CMutableTransaction templateMtx;
|
||||
// prepare self-import 'quasi-burn' tx and also create vout for import tx (in mtx.vout):
|
||||
if (GetSelfimportProof(source, templateMtx, scriptPubKey, proof, rawsourcetx, ivout, sourcetxid, burnAmount) < 0)
|
||||
throw std::runtime_error("Failed validating selfimport");
|
||||
if (GetSelfimportProof(sourceMtx, templateMtx, proofNull) < 0)
|
||||
throw std::runtime_error("Failed creating selfimport template tx");
|
||||
|
||||
vouts = templateMtx.vout;
|
||||
burnOut = MakeBurnOutput(burnAmount, 0xffffffff, ASSETCHAINS_SELFIMPORT, vouts, rawproofEmpty);
|
||||
burnOut = MakeBurnOutput(burnAmount, 0xffffffff, ASSETCHAINS_SELFIMPORT, vouts, rawProofEmpty);
|
||||
templateMtx.vout.clear();
|
||||
templateMtx.vout.push_back(burnOut); // burn tx has only opret with vouts and optional proof
|
||||
|
||||
burnTx = templateMtx; // complete the creation of 'quasi-burn' tx
|
||||
|
||||
std::string hextx = HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(proof, burnTx, vouts)));
|
||||
|
||||
CTxDestination address;
|
||||
bool fValidAddress = ExtractDestination(scriptPubKey, address);
|
||||
|
||||
result.push_back(Pair("sourceTxHex", rawsourcetx));
|
||||
result.push_back(Pair("importTxHex", hextx));
|
||||
result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx
|
||||
result.push_back(Pair("DestinationAddress", EncodeDestination(address))); // notify user about the address where the funds will be sent
|
||||
|
||||
sourceTxHex = HexStr(E_MARSHAL(ss << sourceMtx));
|
||||
importTxHex = HexStr(E_MARSHAL(ss << MakeImportCoinTransaction(proofNull, burnTx, vouts)));
|
||||
|
||||
result.push_back(Pair("SourceTxHex", sourceTxHex));
|
||||
result.push_back(Pair("ImportTxHex", importTxHex));
|
||||
|
||||
return result;
|
||||
}
|
||||
else if (source == ASSETCHAINS_SELFIMPORT)
|
||||
{
|
||||
throw std::runtime_error("not implemented yet\n");
|
||||
|
||||
if (params.size() != 8)
|
||||
throw runtime_error("use \'selfimport rawburntx burntxid nvout rawproof source bindtxid height\' to import from a coin chain\n");
|
||||
|
||||
uint256 bindtxid = Parseuint256((char *)params[6].get_str().c_str());
|
||||
int32_t height = atoi((char *)params[7].get_str().c_str());
|
||||
|
||||
|
||||
// source is external coin is the assetchains symbol in the burnTx OP_RETURN
|
||||
// burnAmount, rawtx and rawproof should be enough for gatewaysdeposit equivalent
|
||||
//std::string hextx = MakeGatewaysImportTx(0, bindtxid, height, source, rawproof, rawsourcetx, ivout, "");
|
||||
|
||||
// result.push_back(Pair("hex", hextx));
|
||||
// result.push_back(Pair("UsedRawtxVout", ivout)); // notify user about the used vout of rawtx
|
||||
return -1;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -536,13 +908,13 @@ UniValue importgatewaydeposit(const UniValue& params, bool fHelp)
|
||||
{
|
||||
UniValue result(UniValue::VOBJ);
|
||||
CMutableTransaction mtx; std::vector<uint8_t> rawproof;
|
||||
std::string hex,coin,rawburntx; int32_t height,burnvout;
|
||||
std::string hex,coin,rawburntx; int32_t height,burnvout; int64_t amount;
|
||||
CPubKey destpub; std::vector<CTxOut> vouts; uint256 bindtxid,burntxid;
|
||||
|
||||
if ( ASSETCHAINS_SELFIMPORT.size() == 0 )
|
||||
throw runtime_error("importgatewaydeposit only works on -ac_import chains");
|
||||
if ( fHelp || params.size() != 8)
|
||||
throw runtime_error("use \'importgatewaydeposit bindtxid height coin burntxid nvout rawburntx rawproof destpub\' to import deposited coins\n");
|
||||
if ( fHelp || params.size() != 9)
|
||||
throw runtime_error("use \'importgatewaydeposit bindtxid height coin burntxid nvout rawburntx rawproof destpub amount\' to import deposited coins\n");
|
||||
if ( ensure_CCrequirements(EVAL_IMPORTGATEWAY) < 0 )
|
||||
throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n");
|
||||
CCerror = "";
|
||||
@@ -554,6 +926,7 @@ UniValue importgatewaydeposit(const UniValue& params, bool fHelp)
|
||||
rawburntx = params[5].get_str();
|
||||
rawproof = ParseHex(params[6].get_str());
|
||||
destpub = ParseHex(params[7].get_str());
|
||||
amount = atof(params[8].get_str().c_str()) * COIN + 0.00000000499999;
|
||||
if (coin == "BEAM" || coin == "CODA")
|
||||
{
|
||||
ERR_RESULT("for BEAM and CODA import use importdual RPC");
|
||||
@@ -564,7 +937,7 @@ UniValue importgatewaydeposit(const UniValue& params, bool fHelp)
|
||||
ERR_RESULT("source coin not equal to ac_import name");
|
||||
return result;
|
||||
}
|
||||
hex = ImportGatewayDeposit(0, bindtxid, height, coin, burntxid, burnvout, rawburntx, rawproof, destpub);
|
||||
hex = ImportGatewayDeposit(0, bindtxid, height, coin, burntxid, burnvout, rawburntx, rawproof, destpub, amount);
|
||||
RETURN_IF_ERROR(CCerror);
|
||||
if ( hex.size() > 0 )
|
||||
{
|
||||
@@ -626,6 +999,7 @@ UniValue importgatewaypartialsign(const UniValue& params, bool fHelp)
|
||||
coin = params[1].get_str();
|
||||
parthex = params[2].get_str();
|
||||
hex = ImportGatewayPartialSign(0,txid,coin,parthex);
|
||||
RETURN_IF_ERROR(CCerror);
|
||||
if ( hex.size() > 0 )
|
||||
{
|
||||
result.push_back(Pair("result", "success"));
|
||||
@@ -933,14 +1307,15 @@ UniValue getimports(const UniValue& params, bool fHelp)
|
||||
{
|
||||
UniValue objTx(UniValue::VOBJ);
|
||||
objTx.push_back(Pair("txid",tx.GetHash().ToString()));
|
||||
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; CTxDestination importaddress;
|
||||
TotalImported += tx.vout[0].nValue;
|
||||
objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[0].nValue)));
|
||||
if (ExtractDestination(tx.vout[0].scriptPubKey, importaddress))
|
||||
ImportProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; CTxDestination importaddress;
|
||||
TotalImported += tx.vout[1].nValue;
|
||||
objTx.push_back(Pair("amount", ValueFromAmount(tx.vout[1].nValue)));
|
||||
if (ExtractDestination(tx.vout[1].scriptPubKey, importaddress))
|
||||
{
|
||||
objTx.push_back(Pair("address", CBitcoinAddress(importaddress).ToString()));
|
||||
}
|
||||
UniValue objBurnTx(UniValue::VOBJ);
|
||||
UniValue objBurnTx(UniValue::VOBJ);
|
||||
CPubKey vinPubkey;
|
||||
if (UnmarshalImportTx(tx, proof, burnTx, payouts))
|
||||
{
|
||||
if (burnTx.vout.size() == 0)
|
||||
@@ -953,8 +1328,14 @@ UniValue getimports(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (rawproof.size() > 0)
|
||||
{
|
||||
std::string sourceSymbol(rawproof.begin(), rawproof.end());
|
||||
std::string sourceSymbol;
|
||||
CTransaction tokenbasetx;
|
||||
E_UNMARSHAL(rawproof, ss >> sourceSymbol;
|
||||
if (!ss.eof())
|
||||
ss >> tokenbasetx );
|
||||
objBurnTx.push_back(Pair("source", sourceSymbol));
|
||||
if( !tokenbasetx.IsNull() )
|
||||
objBurnTx.push_back(Pair("tokenid", tokenbasetx.GetHash().GetHex()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -967,3 +1348,137 @@ UniValue getimports(const UniValue& params, bool fHelp)
|
||||
result.push_back(Pair("time", block.GetBlockTime()));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
// outputs burn transactions in the wallet
|
||||
UniValue getwalletburntransactions(const UniValue& params, bool fHelp)
|
||||
{
|
||||
if (fHelp || params.size() > 1)
|
||||
throw runtime_error(
|
||||
"getwalletburntransactions \"count\"\n\n"
|
||||
"Lists most recent wallet burn transactions up to \'count\' parameter\n"
|
||||
"parameter \'count\' is optional. If omitted, defaults to 10 burn transactions"
|
||||
"\n\n"
|
||||
"\nResult:\n"
|
||||
"[\n"
|
||||
" {\n"
|
||||
" \"txid\": (string)\n"
|
||||
" \"burnedAmount\" : (numeric)\n"
|
||||
" \"targetSymbol\" : (string)\n"
|
||||
" \"targetCCid\" : (numeric)\n"
|
||||
" }\n"
|
||||
"]\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getwalletburntransactions", "100")
|
||||
+ HelpExampleRpc("getwalletburntransactions", "100")
|
||||
+ HelpExampleCli("getwalletburntransactions", "")
|
||||
+ HelpExampleRpc("getwalletburntransactions", "")
|
||||
);
|
||||
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
|
||||
string strAccount = "*";
|
||||
isminefilter filter = ISMINE_SPENDABLE;
|
||||
int nCount = 10;
|
||||
|
||||
if (params.size() == 1)
|
||||
nCount = atoi(params[0].get_str());
|
||||
if (nCount < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count");
|
||||
|
||||
UniValue ret(UniValue::VARR);
|
||||
|
||||
std::list<CAccountingEntry> acentries;
|
||||
CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount);
|
||||
|
||||
// iterate backwards until we have nCount items to return:
|
||||
for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it)
|
||||
{
|
||||
CWalletTx *const pwtx = (*it).second.first;
|
||||
if (pwtx != 0)
|
||||
{
|
||||
LOGSTREAM("importcoin", CCLOG_DEBUG2, stream << "pwtx iterpos=" << (int32_t)pwtx->nOrderPos << " txid=" << pwtx->GetHash().GetHex() << std::endl);
|
||||
vscript_t vopret;
|
||||
std::string targetSymbol;
|
||||
uint32_t targetCCid; uint256 payoutsHash;
|
||||
std::vector<uint8_t> rawproof;
|
||||
|
||||
if (pwtx->vout.size() > 0 && GetOpReturnData(pwtx->vout.back().scriptPubKey, vopret) && !vopret.empty() &&
|
||||
UnmarshalBurnTx(*pwtx, targetSymbol, &targetCCid, payoutsHash, rawproof)) {
|
||||
UniValue entry(UniValue::VOBJ);
|
||||
entry.push_back(Pair("txid", pwtx->GetHash().GetHex()));
|
||||
if (vopret.begin()[0] == EVAL_TOKENS) {
|
||||
// get burned token value
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
uint256 tokenid;
|
||||
uint8_t evalCodeInOpret;
|
||||
std::vector<CPubKey> voutTokenPubkeys;
|
||||
|
||||
//skip token opret:
|
||||
if (DecodeTokenOpRet(pwtx->vout.back().scriptPubKey, evalCodeInOpret, tokenid, voutTokenPubkeys, oprets) != 0) {
|
||||
CTransaction tokenbasetx;
|
||||
uint256 hashBlock;
|
||||
|
||||
if (myGetTransaction(tokenid, tokenbasetx, hashBlock)) {
|
||||
std::vector<uint8_t> vorigpubkey;
|
||||
std::string name, description;
|
||||
std::vector<std::pair<uint8_t, vscript_t>> oprets;
|
||||
|
||||
if (tokenbasetx.vout.size() > 0 &&
|
||||
DecodeTokenCreateOpRet(tokenbasetx.vout.back().scriptPubKey, vorigpubkey, name, description, oprets) == 'c')
|
||||
{
|
||||
uint8_t destEvalCode = EVAL_TOKENS; // init set to fungible token:
|
||||
vscript_t vopretNonfungible;
|
||||
GetOpretBlob(oprets, OPRETID_NONFUNGIBLEDATA, vopretNonfungible);
|
||||
if (!vopretNonfungible.empty())
|
||||
destEvalCode = vopretNonfungible.begin()[0];
|
||||
|
||||
int64_t burnAmount = 0;
|
||||
for (auto v : pwtx->vout)
|
||||
if (v.scriptPubKey.IsPayToCryptoCondition() &&
|
||||
CTxOut(v.nValue, v.scriptPubKey) == MakeTokensCC1vout(destEvalCode ? destEvalCode : EVAL_TOKENS, v.nValue, pubkey2pk(ParseHex(CC_BURNPUBKEY)))) // burned to dead pubkey
|
||||
burnAmount += v.nValue;
|
||||
|
||||
entry.push_back(Pair("burnedAmount", ValueFromAmount(burnAmount)));
|
||||
entry.push_back(Pair("tokenid", tokenid.GetHex()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
entry.push_back(Pair("burnedAmount", ValueFromAmount(pwtx->vout.back().nValue))); // coins
|
||||
entry.push_back(Pair("targetSymbol", targetSymbol));
|
||||
entry.push_back(Pair("targetCCid", std::to_string(targetCCid)));
|
||||
if (mytxid_inmempool(pwtx->GetHash()))
|
||||
entry.push_back(Pair("inMempool", "yes"));
|
||||
ret.push_back(entry);
|
||||
}
|
||||
} //else fprintf(stderr,"null pwtx\n
|
||||
if ((int)ret.size() >= (nCount))
|
||||
break;
|
||||
}
|
||||
// ret is newest to oldest
|
||||
|
||||
if (nCount > (int)ret.size())
|
||||
nCount = ret.size();
|
||||
|
||||
vector<UniValue> arrTmp = ret.getValues();
|
||||
|
||||
vector<UniValue>::iterator first = arrTmp.begin();
|
||||
vector<UniValue>::iterator last = arrTmp.begin();
|
||||
std::advance(last, nCount);
|
||||
|
||||
if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end());
|
||||
if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first);
|
||||
|
||||
std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest
|
||||
|
||||
ret.clear();
|
||||
ret.setArray();
|
||||
ret.push_backV(arrTmp);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -49,8 +49,10 @@
|
||||
using namespace std;
|
||||
|
||||
#include "komodo_defs.h"
|
||||
extern int32_t ASSETCHAINS_FOUNDERS;
|
||||
|
||||
extern int32_t ASSETCHAINS_FOUNDERS;
|
||||
uint64_t komodo_commission(const CBlock *pblock,int32_t height);
|
||||
int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex);
|
||||
arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc);
|
||||
|
||||
/**
|
||||
@@ -1009,6 +1011,7 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp)
|
||||
"\nResult:\n"
|
||||
"{\n"
|
||||
" \"miner\" : x.xxx (numeric) The mining reward amount in KMD.\n"
|
||||
" \"ac_pubkey\" : x.xxx (numeric) The mining reward amount in KMD.\n"
|
||||
"}\n"
|
||||
"\nExamples:\n"
|
||||
+ HelpExampleCli("getblocksubsidy", "1000")
|
||||
@@ -1019,11 +1022,32 @@ UniValue getblocksubsidy(const UniValue& params, bool fHelp)
|
||||
int nHeight = (params.size()==1) ? params[0].get_int() : chainActive.Height();
|
||||
if (nHeight < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Block height out of range");
|
||||
|
||||
|
||||
CAmount nFoundersReward = 0;
|
||||
CAmount nReward = GetBlockSubsidy(nHeight, Params().GetConsensus());
|
||||
UniValue result(UniValue::VOBJ);
|
||||
result.push_back(Pair("miner", ValueFromAmount(nReward)));
|
||||
//result.push_back(Pair("founders", ValueFromAmount(nFoundersReward)));
|
||||
|
||||
if ( strlen(ASSETCHAINS_OVERRIDE_PUBKEY.c_str()) == 66 || ASSETCHAINS_SCRIPTPUB.size() > 1 )
|
||||
{
|
||||
if ( ASSETCHAINS_FOUNDERS == 0 && ASSETCHAINS_COMMISSION != 0 )
|
||||
{
|
||||
// ac comission chains need the block to exist to calulate the reward.
|
||||
if ( nHeight <= chainActive.Height() )
|
||||
{
|
||||
CBlockIndex* pblockIndex = chainActive[nHeight];
|
||||
CBlock block;
|
||||
if ( komodo_blockload(block, pblockIndex) == 0 )
|
||||
nFoundersReward = komodo_commission(&block, nHeight);
|
||||
}
|
||||
}
|
||||
else if ( ASSETCHAINS_FOUNDERS != 0 )
|
||||
{
|
||||
// Assetchains founders chains have a fixed reward so can be calculated at any given height.
|
||||
nFoundersReward = komodo_commission(0, nHeight);
|
||||
}
|
||||
result.push_back(Pair("ac_pubkey", ValueFromAmount(nFoundersReward)));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -203,7 +203,7 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue&
|
||||
in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
|
||||
else if (tx.IsCoinImport()) {
|
||||
in.push_back(Pair("is_import", "1"));
|
||||
TxProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; CTxDestination importaddress;
|
||||
ImportProof proof; CTransaction burnTx; std::vector<CTxOut> payouts; CTxDestination importaddress;
|
||||
if (UnmarshalImportTx(tx, proof, burnTx, payouts))
|
||||
{
|
||||
if (burnTx.vout.size() == 0)
|
||||
|
||||
@@ -350,9 +350,13 @@ static const CRPCCommand vRPCCommands[] =
|
||||
{ "crosschain", "getNotarisationsForBlock", &getNotarisationsForBlock, true },
|
||||
{ "crosschain", "scanNotarisationsDB", &scanNotarisationsDB, true },
|
||||
{ "crosschain", "getimports", &getimports, true },
|
||||
{ "crosschain", "getwalletburntransactions", &getwalletburntransactions, true },
|
||||
{ "crosschain", "migrate_converttoexport", &migrate_converttoexport, true },
|
||||
{ "crosschain", "migrate_createburntransaction", &migrate_createburntransaction, true },
|
||||
{ "crosschain", "migrate_createimporttransaction", &migrate_createimporttransaction, true },
|
||||
{ "crosschain", "migrate_completeimporttransaction", &migrate_completeimporttransaction, true },
|
||||
{ "crosschain", "migrate_checkburntransactionsource", &migrate_checkburntransactionsource, true },
|
||||
{ "crosschain", "migrate_createnotaryapprovaltransaction", &migrate_createnotaryapprovaltransaction, true },
|
||||
{ "crosschain", "selfimport", &selfimport, true },
|
||||
{ "crosschain", "importdual", &importdual, true },
|
||||
//ImportGateway
|
||||
|
||||
@@ -478,9 +478,13 @@ extern UniValue crosschainproof(const UniValue& params, bool fHelp);
|
||||
extern UniValue getNotarisationsForBlock(const UniValue& params, bool fHelp);
|
||||
extern UniValue scanNotarisationsDB(const UniValue& params, bool fHelp);
|
||||
extern UniValue getimports(const UniValue& params, bool fHelp);
|
||||
extern UniValue getwalletburntransactions(const UniValue& params, bool fHelp);
|
||||
extern UniValue migrate_converttoexport(const UniValue& params, bool fHelp);
|
||||
extern UniValue migrate_createburntransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp);
|
||||
extern UniValue migrate_checkburntransactionsource(const UniValue& params, bool fHelp);
|
||||
extern UniValue migrate_createnotaryapprovaltransaction(const UniValue& params, bool fHelp);
|
||||
|
||||
extern UniValue notaries(const UniValue& params, bool fHelp);
|
||||
extern UniValue minerids(const UniValue& params, bool fHelp);
|
||||
|
||||
@@ -94,14 +94,15 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
return NullUniValue;
|
||||
|
||||
if (fHelp || params.size() < 1 || params.size() > 3)
|
||||
if (fHelp || params.size() < 1 || params.size() > 4)
|
||||
throw runtime_error(
|
||||
"importprivkey \"komodoprivkey\" ( \"label\" rescan )\n"
|
||||
"importprivkey \"komodoprivkey\" ( \"label\" rescan height)\n"
|
||||
"\nAdds a private key (as returned by dumpprivkey) to your wallet.\n"
|
||||
"\nArguments:\n"
|
||||
"1. \"komodoprivkey\" (string, required) The private key (see dumpprivkey)\n"
|
||||
"2. \"label\" (string, optional, default=\"\") An optional label\n"
|
||||
"3. rescan (boolean, optional, default=true) Rescan the wallet for transactions\n"
|
||||
"4. height (integer, optional, default=0) start at block height?\n"
|
||||
"\nNote: This call can take minutes to complete if rescan is true.\n"
|
||||
"\nExamples:\n"
|
||||
"\nDump a private key\n"
|
||||
@@ -111,7 +112,11 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
|
||||
"\nImport using a label and without rescan\n"
|
||||
+ HelpExampleCli("importprivkey", "\"mykey\" \"testing\" false") +
|
||||
"\nAs a JSON-RPC call\n"
|
||||
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false")
|
||||
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", false") +
|
||||
"\nImport with rescan from a block height\n"
|
||||
+ HelpExampleCli("importprivkey", "\"mykey\" \"testing\" true 1000") +
|
||||
"\nAs a JSON-RPC call\n"
|
||||
+ HelpExampleRpc("importprivkey", "\"mykey\", \"testing\", true, 1000")
|
||||
);
|
||||
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
@@ -120,6 +125,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
|
||||
|
||||
string strSecret = params[0].get_str();
|
||||
string strLabel = "";
|
||||
int32_t height = 0;
|
||||
if (params.size() > 1)
|
||||
strLabel = params[1].get_str();
|
||||
|
||||
@@ -127,7 +133,12 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
|
||||
bool fRescan = true;
|
||||
if (params.size() > 2)
|
||||
fRescan = params[2].get_bool();
|
||||
if ( fRescan && params.size() == 4 )
|
||||
height = params[3].get_int();
|
||||
|
||||
if ( height < 0 || height > chainActive.Height() )
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan height is out of range.");
|
||||
|
||||
CKey key = DecodeSecret(strSecret);
|
||||
if (!key.IsValid()) throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key encoding");
|
||||
|
||||
@@ -152,7 +163,7 @@ UniValue importprivkey(const UniValue& params, bool fHelp)
|
||||
pwalletMain->nTimeFirstKey = 1; // 0 would be considered 'no value'
|
||||
|
||||
if (fRescan) {
|
||||
pwalletMain->ScanForWalletTransactions(chainActive.Genesis(), true);
|
||||
pwalletMain->ScanForWalletTransactions(chainActive[height], true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6594,6 +6594,7 @@ UniValue gatewayspartialsign(const UniValue& params, bool fHelp)
|
||||
coin = params[1].get_str();
|
||||
parthex = params[2].get_str();
|
||||
hex = GatewaysPartialSign(0,txid,coin,parthex);
|
||||
RETURN_IF_ERROR(CCerror);
|
||||
if ( hex.size() > 0 )
|
||||
{
|
||||
result.push_back(Pair("result", "success"));
|
||||
|
||||
@@ -16,6 +16,11 @@ bool CZMQAbstractNotifier::NotifyBlock(const CBlockIndex * /*CBlockIndex*/)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CZMQAbstractNotifier::NotifyBlock(const CBlock &)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CZMQAbstractNotifier::NotifyTransaction(const CTransaction &/*transaction*/)
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
virtual void Shutdown() = 0;
|
||||
|
||||
virtual bool NotifyBlock(const CBlockIndex *pindex);
|
||||
virtual bool NotifyBlock(const CBlock& pblock);
|
||||
virtual bool NotifyTransaction(const CTransaction &transaction);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -39,6 +39,7 @@ CZMQNotificationInterface* CZMQNotificationInterface::CreateWithArguments(const
|
||||
factories["pubhashtx"] = CZMQAbstractNotifier::Create<CZMQPublishHashTransactionNotifier>;
|
||||
factories["pubrawblock"] = CZMQAbstractNotifier::Create<CZMQPublishRawBlockNotifier>;
|
||||
factories["pubrawtx"] = CZMQAbstractNotifier::Create<CZMQPublishRawTransactionNotifier>;
|
||||
factories["pubcheckedblock"] = CZMQAbstractNotifier::Create<CZMQPublishCheckedBlockNotifier>;
|
||||
|
||||
for (std::map<std::string, CZMQNotifierFactory>::const_iterator i=factories.begin(); i!=factories.end(); ++i)
|
||||
{
|
||||
@@ -141,6 +142,27 @@ void CZMQNotificationInterface::UpdatedBlockTip(const CBlockIndex *pindex)
|
||||
}
|
||||
}
|
||||
|
||||
void CZMQNotificationInterface::BlockChecked(const CBlock& block, const CValidationState& state)
|
||||
{
|
||||
if (state.IsInvalid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
|
||||
{
|
||||
CZMQAbstractNotifier *notifier = *i;
|
||||
if (notifier->NotifyBlock(block))
|
||||
{
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
notifier->Shutdown();
|
||||
i = notifiers.erase(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CZMQNotificationInterface::SyncTransaction(const CTransaction &tx, const CBlock *pblock)
|
||||
{
|
||||
for (std::list<CZMQAbstractNotifier*>::iterator i = notifiers.begin(); i!=notifiers.end(); )
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#define BITCOIN_ZMQ_ZMQNOTIFICATIONINTERFACE_H
|
||||
|
||||
#include "validationinterface.h"
|
||||
#include "consensus/validation.h"
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
@@ -26,6 +27,7 @@ protected:
|
||||
// CValidationInterface
|
||||
void SyncTransaction(const CTransaction &tx, const CBlock *pblock);
|
||||
void UpdatedBlockTip(const CBlockIndex *pindex);
|
||||
void BlockChecked(const CBlock& block, const CValidationState& state);
|
||||
|
||||
private:
|
||||
CZMQNotificationInterface();
|
||||
|
||||
@@ -12,6 +12,7 @@ static const char *MSG_HASHBLOCK = "hashblock";
|
||||
static const char *MSG_HASHTX = "hashtx";
|
||||
static const char *MSG_RAWBLOCK = "rawblock";
|
||||
static const char *MSG_RAWTX = "rawtx";
|
||||
static const char *MSG_CHECKEDBLOCK = "checkedblock";
|
||||
|
||||
// Internal function to send multipart message
|
||||
static int zmq_send_multipart(void *sock, const void* data, size_t size, ...)
|
||||
@@ -179,6 +180,19 @@ bool CZMQPublishRawBlockNotifier::NotifyBlock(const CBlockIndex *pindex)
|
||||
return SendMessage(MSG_RAWBLOCK, &(*ss.begin()), ss.size());
|
||||
}
|
||||
|
||||
bool CZMQPublishCheckedBlockNotifier::NotifyBlock(const CBlock& block)
|
||||
{
|
||||
LogPrint("zmq", "zmq: Publish checkedblock %s\n", block.GetHash().GetHex());
|
||||
|
||||
CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
|
||||
{
|
||||
LOCK(cs_main);
|
||||
ss << block;
|
||||
}
|
||||
|
||||
return SendMessage(MSG_CHECKEDBLOCK, &(*ss.begin()), ss.size());
|
||||
}
|
||||
|
||||
bool CZMQPublishRawTransactionNotifier::NotifyTransaction(const CTransaction &transaction)
|
||||
{
|
||||
uint256 hash = transaction.GetHash();
|
||||
|
||||
@@ -52,4 +52,10 @@ public:
|
||||
bool NotifyTransaction(const CTransaction &transaction);
|
||||
};
|
||||
|
||||
class CZMQPublishCheckedBlockNotifier : public CZMQAbstractPublishNotifier
|
||||
{
|
||||
public:
|
||||
bool NotifyBlock(const CBlock &block);
|
||||
};
|
||||
|
||||
#endif // BITCOIN_ZMQ_ZMQPUBLISHNOTIFIER_H
|
||||
|
||||
Reference in New Issue
Block a user