diff --git a/.gitignore b/.gitignore index bebcef932..3916d4e87 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,4 @@ src/komodo-cli src/komodod src/komodo-tx src/komodo-test +src/wallet-utility diff --git a/src/Makefile.am b/src/Makefile.am index 231386c96..fa475b4e1 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -256,10 +256,11 @@ libbitcoin_server_a_SOURCES = \ asyncrpcqueue.cpp \ bloom.cpp \ cc/eval.cpp \ - cc/importcoin.cpp \ + cc/import.cpp \ cc/betprotocol.cpp \ chain.cpp \ checkpoints.cpp \ + crosschain.cpp \ deprecation.cpp \ httprpc.cpp \ httpserver.cpp \ @@ -271,6 +272,7 @@ libbitcoin_server_a_SOURCES = \ miner.cpp \ net.cpp \ noui.cpp \ + notarisationdb.cpp \ paymentdisclosure.cpp \ paymentdisclosuredb.cpp \ policy/fees.cpp \ @@ -384,6 +386,7 @@ libbitcoin_common_a_SOURCES = \ core_read.cpp \ core_write.cpp \ hash.cpp \ + importcoin.cpp \ key.cpp \ keystore.cpp \ netbase.cpp \ diff --git a/src/Makefile.ktest.include b/src/Makefile.ktest.include index a6d2d1829..93d3460e2 100644 --- a/src/Makefile.ktest.include +++ b/src/Makefile.ktest.include @@ -9,7 +9,8 @@ komodo_test_SOURCES = \ test-komodo/test_cryptoconditions.cpp \ test-komodo/test_coinimport.cpp \ test-komodo/test_eval_bet.cpp \ - test-komodo/test_eval_notarisation.cpp + test-komodo/test_eval_notarisation.cpp \ + test-komodo/test_crosschain.cpp komodo_test_CPPFLAGS = $(komodod_CPPFLAGS) diff --git a/src/cc/betprotocol.cpp b/src/cc/betprotocol.cpp index c3be8be8f..1b4834767 100644 --- a/src/cc/betprotocol.cpp +++ b/src/cc/betprotocol.cpp @@ -5,8 +5,9 @@ #include "chain.h" #include "streams.h" #include "script/cc.h" -#include "cc/eval.h" #include "cc/betprotocol.h" +#include "cc/eval.h" +#include "cc/utils.h" #include "primitives/transaction.h" diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index e98b0dc6b..6dc72f2a0 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -1,9 +1,11 @@ #include #include +#include "primitives/block.h" #include "primitives/transaction.h" #include "script/cc.h" #include "cc/eval.h" +#include "cc/utils.h" #include "main.h" #include "chain.h" #include "core_io.h" @@ -14,9 +16,7 @@ Eval* EVAL_TEST = 0; bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn) { - Eval eval_; - Eval *eval = EVAL_TEST; - if (!eval) eval = &eval_; + EvalRef eval; bool out = eval->Dispatch(cond, tx, nIn); assert(eval->state.IsValid() == out); @@ -162,8 +162,7 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) CBlockIndex block; if (!GetTxConfirmed(notaryHash, notarisationTx, block)) return false; if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.nTime)) return false; - if (notarisationTx.vout.size() < 2) return false; - if (!data.Parse(notarisationTx.vout[1].scriptPubKey)) return false; + if (!ParseNotarisationOpReturn(notarisationTx, data)) return false; return true; } @@ -176,34 +175,13 @@ bool Eval::GetNotarisationData(int notarisationHeight, NotarisationData &data, b /* * Notarisation data, ie, OP_RETURN payload in notarisation transactions */ -extern char ASSETCHAINS_SYMBOL[16]; - -bool NotarisationData::Parse(const CScript scriptPK) +bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data) { - *this = NotarisationData(); - + if (tx.vout.size() < 2) return false; std::vector vdata; - if (!GetOpReturnData(scriptPK, vdata)) return false; - - CDataStream ss(vdata, SER_NETWORK, PROTOCOL_VERSION); - - try { - ss >> blockHash; - ss >> height; - if (ASSETCHAINS_SYMBOL[0]) - ss >> txHash; - - char *nullPos = (char*) memchr(&ss[0], 0, ss.size()); - if (!nullPos) return false; - ss.read(symbol, nullPos-&ss[0]+1); - - if (ss.size() < 36) return false; - ss >> MoM; - ss >> MoMDepth; - } catch (...) { - return false; - } - return true; + if (!GetOpReturnData(tx.vout[1].scriptPubKey, vdata)) return false; + bool out = E_UNMARSHAL(vdata, ss >> data); + return out; } @@ -240,3 +218,11 @@ uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector& vMerkleB } return hash; } + + +uint256 GetMerkleRoot(const std::vector& vLeaves) +{ + bool fMutated; + std::vector vMerkleTree; + return BuildMerkleTree(&fMutated, vLeaves, vMerkleTree); +} diff --git a/src/cc/eval.h b/src/cc/eval.h index c65e5f863..1bbf68f65 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -3,6 +3,7 @@ #include +#include "cc/utils.h" #include "chain.h" #include "streams.h" #include "version.h" @@ -78,6 +79,23 @@ public: }; +extern Eval* EVAL_TEST; + + +/* + * Get a pointer to an Eval to use + */ +typedef std::unique_ptr EvalRef_; +class EvalRef : public EvalRef_ +{ +public: + EvalRef() : EvalRef_( + EVAL_TEST ? EVAL_TEST : new Eval(), + [](Eval* e){if (e!=EVAL_TEST) delete e;}) { } +}; + + + bool RunCCEval(const CC *cond, const CTransaction &tx, unsigned int nIn); @@ -97,24 +115,68 @@ public: evaluate(std::vector header, std::vector body) = 0; }; + +extern char ASSETCHAINS_SYMBOL[65]; + + /* - * Data from notarisation OP_RETURN + * Data from notarisation OP_RETURN from chain being notarised */ -class NotarisationData { +class NotarisationData +{ public: + bool IsBackNotarisation = 0; uint256 blockHash; uint32_t height; uint256 txHash; // Only get this guy in asset chains not in KMD - char symbol[64]; + char symbol[64] = "\0"; uint256 MoM; uint32_t MoMDepth; + uint32_t ccId; uint256 MoMoM; uint32_t MoMoMDepth; - bool Parse(CScript scriptPubKey); + NotarisationData(bool IsBack=0) : IsBackNotarisation(IsBack) {} + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(blockHash); + READWRITE(height); + if (IsBackNotarisation || (!ser_action.ForRead() && !txHash.IsNull())) + READWRITE(txHash); + SerSymbol(s, ser_action); + READWRITE(MoM); + READWRITE(MoMDepth); + if (s.size() == 0) return; + READWRITE(ccId); + if (IsBackNotarisation) { + READWRITE(MoMoM); + READWRITE(MoMoMDepth); + } + } + + template + void SerSymbol(Stream& s, CSerActionSerialize act) + { + s.write(symbol, strlen(symbol)+1); + } + + template + void SerSymbol(Stream& s, CSerActionUnserialize act) + { + char *nullPos = (char*) memchr(&s[0], 0, s.size()); + if (!nullPos) + throw std::ios_base::failure("couldn't parse symbol"); + s.read(symbol, nullPos-&s[0]+1); + } }; +bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data); + + /* * Eval code utilities. */ @@ -126,31 +188,6 @@ FOREACH_EVAL(EVAL_GENERATE_DEF); std::string EvalToStr(EvalCode c); -/* - * Serialisation boilerplate - */ -#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;}) -template -std::vector SerializeF(const T f) -{ - CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); - f(ss); - return std::vector(ss.begin(), ss.end()); -} - -#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;}) -template -bool DeserializeF(const std::vector vIn, T f) -{ - CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION); - try { - f(ss); - if (ss.eof()) return true; - } catch(...) {} - return false; -} - - /* * Merkle stuff */ @@ -167,6 +204,13 @@ public: MerkleBranch(int i, std::vector b) : nIndex(i), branch(b) {} uint256 Exec(uint256 hash) const { return SafeCheckMerkleBranch(hash, branch, nIndex); } + MerkleBranch& operator<<(MerkleBranch append) + { + nIndex += append.nIndex << branch.size(); + branch.insert(branch.end(), append.branch.begin(), append.branch.end()); + return *this; + } + ADD_SERIALIZE_METHODS; template @@ -177,4 +221,7 @@ public: }; +uint256 GetMerkleRoot(const std::vector& vLeaves); + + #endif /* CC_EVAL_H */ diff --git a/src/cc/import.cpp b/src/cc/import.cpp new file mode 100644 index 000000000..be4929b94 --- /dev/null +++ b/src/cc/import.cpp @@ -0,0 +1,70 @@ +#include "cc/eval.h" +#include "cc/utils.h" +#include "importcoin.h" +#include "primitives/transaction.h" + + +/* + * 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. + */ +bool Eval::ImportCoin(const std::vector params, const CTransaction &importTx, unsigned int nIn) +{ + if (importTx.vout.size() == 0) return Invalid("no-vouts"); + + // params + MomoProof proof; + CTransaction burnTx; + if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx)) + return Invalid("invalid-params"); + + // Control all aspects of this transaction + // It must not be at all malleable + if (MakeImportCoinTransaction(proof, burnTx, importTx.vout).GetHash() != importTx.GetHash()) + return Invalid("non-canonical"); + + // burn params + uint32_t chain; // todo + uint256 payoutsHash; + std::vector burnOpret; + if (burnTx.vout.size() == 0) return Invalid("invalid-burn-outputs"); + GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret); + if (!E_UNMARSHAL(burnOpret, ss >> VARINT(chain); ss >> payoutsHash)) + return Invalid("invalid-burn-params"); + + // check chain + if (chain != GetCurrentLedgerID()) + return Invalid("importcoin-wrong-chain"); + + // check burn amount + { + uint64_t burnAmount = burnTx.vout[0].nValue; + if (burnAmount == 0) + return Invalid("invalid-burn-amount"); + uint64_t totalOut = 0; + for (int i=0; i burnAmount) + return Invalid("payout-too-high"); + } + + // Check burntx shows correct outputs hash + if (payoutsHash != SerializeHash(importTx.vout)) + return Invalid("wrong-payouts"); + + // Check proof confirms existance of burnTx + { + NotarisationData data; + if (!GetNotarisationData(proof.notarisationHeight, data, true)) + return Invalid("coudnt-load-momom"); + + if (data.MoMoM != proof.branch.Exec(burnTx.GetHash())) + return Invalid("momom-check-fail"); + } + + return Valid(); +} + + diff --git a/src/cc/utils.cpp b/src/cc/utils.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/cc/utils.h b/src/cc/utils.h new file mode 100644 index 000000000..5704016e4 --- /dev/null +++ b/src/cc/utils.h @@ -0,0 +1,34 @@ +#ifndef CC_UTILS_H +#define CC_UTILS_H + +#include "streams.h" +#include "version.h" + + +/* + * Serialisation boilerplate + */ + +template +std::vector SerializeF(const T f) +{ + CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); + f(ss); + return std::vector(ss.begin(), ss.end()); +} + +template +bool DeserializeF(const std::vector vIn, T f) +{ + CDataStream ss(vIn, SER_NETWORK, PROTOCOL_VERSION); + try { + f(ss); + if (ss.eof()) return true; + } catch(...) {} + return false; +} + +#define E_MARSHAL(body) SerializeF([&] (CDataStream &ss) {body;}) +#define E_UNMARSHAL(params, body) DeserializeF(params, [&] (CDataStream &ss) {body;}) + +#endif /* CC_UTILS_H */ diff --git a/src/coins.cpp b/src/coins.cpp index b8f5ffe2c..8b5af804c 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -9,7 +9,7 @@ #include "version.h" #include "policy/fees.h" #include "komodo_defs.h" -#include "cc/importcoin.h" +#include "importcoin.h" #include diff --git a/src/crosschain.cpp b/src/crosschain.cpp new file mode 100644 index 000000000..069a53f38 --- /dev/null +++ b/src/crosschain.cpp @@ -0,0 +1,217 @@ +#include "cc/eval.h" +#include "main.h" +#include "notarisationdb.h" + + +/* On KMD */ +uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vector &moms, int* assetChainHeight) +{ + /* + * Notaries don't wait for confirmation on KMD before performing a backnotarisation, + * but we need a determinable range that will encompass all merkle roots. Include MoMs + * including the block height of the last notarisation until the height before the + * previous notarisation. + */ + if (targetCCid <= 1) + return uint256(); + + int seenOwnNotarisations = 0; + + // TODO: test height out of range + // TODO: Make sure that boundary for moms is notarisation tx not block + + for (int i=0; i<1440; i++) { + if (i > kmdHeight) break; + NotarisationsInBlock notarisations; + uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock; + if (!pnotarisations->Read(blockHash, notarisations)) + continue; + BOOST_FOREACH(Notarisation& nota, notarisations) { + NotarisationData& data = nota.second; + if (data.ccId != targetCCid) + continue; + if (strcmp(data.symbol, symbol) == 0) + { + seenOwnNotarisations++; + if (seenOwnNotarisations == 2) + goto end; + if (seenOwnNotarisations == 1) + *assetChainHeight = data.height; // TODO: Needed? + continue; // Don't include own MoMs + } + if (seenOwnNotarisations == 1) + moms.push_back(data.MoM); + } + } + +end: + return GetMerkleRoot(moms); +} + + +/* On KMD */ +std::pair GetCrossChainProof(uint256 txid, char* targetSymbol, + uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof) +{ + /* + * Here we are given a proof generated by an assetchain A which goes from given txid to + * an assetchain MoM. We need to go from the notarisationTxid for A to the MoMoM range of the + * backnotarisation for B (given by kmdheight of notarisation), find the MoM within the MoMs for + * that range, and finally extend the proof to lead to the MoMoM (proof root). + */ + EvalRef eval; + uint256 MoM = assetChainProof.Exec(txid); + + // Get a kmd height for given notarisation Txid + int kmdHeight; + { + CTransaction sourceNotarisation; + uint256 hashBlock; + CBlockIndex blockIdx; + if (eval->GetTxConfirmed(notarisationTxid, sourceNotarisation, blockIdx)) + kmdHeight = blockIdx.nHeight; + else if (eval->GetTxUnconfirmed(notarisationTxid, sourceNotarisation, hashBlock)) + kmdHeight = chainActive.Tip()->nHeight; + else + throw std::runtime_error("Notarisation not found"); + } + + // Get MoMs for kmd height and symbol + std::vector moms; + int targetChainStartHeight; + uint256 MoMoM = GetProofRoot(targetSymbol, targetCCid, kmdHeight, moms, &targetChainStartHeight); + if (MoMoM.IsNull()) + throw std::runtime_error("No MoMs found"); + + // Find index of source MoM in MoMoM + int nIndex; + for (nIndex=0; nIndex newBranch; + { + CBlock fakeBlock; + for (int i=0; i + */ +std::pair GetAssetchainProof(uint256 hash) +{ + uint256 notarisationHash, MoM,MoMoM; int32_t notarisedHeight, depth; CBlockIndex* blockIndex; + std::vector branch; + int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; + + { + uint256 blockHash; + CTransaction tx; + if (!GetTransaction(hash, tx, blockHash, true)) + throw std::runtime_error("cannot find transaction"); + + blockIndex = mapBlockIndex[blockHash]; + + depth = komodo_MoM(¬arisedHeight, &MoM, ¬arisationHash, blockIndex->nHeight,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi); + + if (!depth) + throw std::runtime_error("notarisation not found"); + + // index of block in MoM leaves + nIndex = notarisedHeight - blockIndex->nHeight; + } + + // build merkle chain from blocks to MoM + { + // since the merkle branch code is tied up in a block class + // and we want to make a merkle branch for something that isnt transactions + CBlock fakeBlock; + for (int i=0; ihashMerkleRoot; + CTransaction fakeTx; + // first value in CTransaction memory is it's hash + memcpy((void*)&fakeTx, mRoot.begin(), 32); + fakeBlock.vtx.push_back(fakeTx); + } + branch = fakeBlock.GetMerkleBranch(nIndex); + + // Check branch + if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex)) + throw std::runtime_error("Failed merkle block->MoM"); + } + + // Now get the tx merkle branch + { + CBlock block; + + if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0) + throw std::runtime_error("Block not available (pruned data)"); + + if(!ReadBlockFromDisk(block, blockIndex,1)) + throw std::runtime_error("Can't read block from disk"); + + // Locate the transaction in the block + int nTxIndex; + for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++) + if (block.vtx[nTxIndex].GetHash() == hash) + break; + + if (nTxIndex == (int)block.vtx.size()) + throw std::runtime_error("Error locating tx in block"); + + std::vector txBranch = block.GetMerkleBranch(nTxIndex); + + // Check branch + if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex)) + throw std::runtime_error("Failed merkle tx->block"); + + // concatenate branches + nIndex = (nIndex << txBranch.size()) + nTxIndex; + branch.insert(branch.begin(), txBranch.begin(), txBranch.end()); + } + + // Check the proof + if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex)) + throw std::runtime_error("Failed validating MoM"); + + // All done! + CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); + return std::make_pair(notarisationHash, MerkleBranch(nIndex, branch)); +} diff --git a/src/crosschain.h b/src/crosschain.h new file mode 100644 index 000000000..25fe50e5f --- /dev/null +++ b/src/crosschain.h @@ -0,0 +1,21 @@ +#ifndef CROSSCHAIN_H +#define CROSSCHAIN_H + +#include "cc/eval.h" + + +/* On assetchain */ +std::pair GetAssetchainProof(uint256 hash); + +/* On KMD */ +uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vector &moms, int* assetChainHeight); + +/* On KMD */ +std::pair GetCrossChainProof(uint256 txid, char* targetSymbol, + uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof); + +/* On assetchain */ +bool ValidateCrossChainProof(uint256 txid, int notarisationHeight, MerkleBranch proof); + + +#endif /* CROSSCHAIN_H */ diff --git a/src/cc/importcoin.cpp b/src/importcoin.cpp similarity index 61% rename from src/cc/importcoin.cpp rename to src/importcoin.cpp index d5f626683..3874ed7d6 100644 --- a/src/cc/importcoin.cpp +++ b/src/importcoin.cpp @@ -1,4 +1,5 @@ -#include "cc/importcoin.h" +#include "importcoin.h" +#include "cc/utils.h" #include "coins.h" #include "hash.h" #include "script/cc.h" @@ -29,71 +30,6 @@ CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector } -/* - * CC Eval method for import coin. - * - * This method has to control *every* parameter of the ImportCoin transaction, so that the legal - * importTx for a valid burnTx is 1:1. There can be no two legal importTx transactions for a burnTx - * on another chain. - */ -bool Eval::ImportCoin(const std::vector params, const CTransaction &importTx, unsigned int nIn) -{ - if (importTx.vout.size() == 0) return Invalid("no-vouts"); - - // params - MomoProof proof; - CTransaction burnTx; - if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx)) - return Invalid("invalid-params"); - - // Control all aspects of this transaction - // It must not be at all malleable - if (MakeImportCoinTransaction(proof, burnTx, importTx.vout).GetHash() != importTx.GetHash()) - return Invalid("non-canonical"); - - // burn params - uint32_t chain; // todo - uint256 payoutsHash; - std::vector burnOpret; - if (burnTx.vout.size() == 0) return Invalid("invalid-burn-outputs"); - GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret); - if (!E_UNMARSHAL(burnOpret, ss >> VARINT(chain); ss >> payoutsHash)) - return Invalid("invalid-burn-params"); - - // check chain - if (chain != GetCurrentLedgerID()) - return Invalid("importcoin-wrong-chain"); - - // check burn amount - { - uint64_t burnAmount = burnTx.vout[0].nValue; - if (burnAmount == 0) - return Invalid("invalid-burn-amount"); - uint64_t totalOut = 0; - for (int i=0; i burnAmount) - return Invalid("payout-too-high"); - } - - // Check burntx shows correct outputs hash - if (payoutsHash != SerializeHash(importTx.vout)) - return Invalid("wrong-payouts"); - - // Check proof confirms existance of burnTx - { - NotarisationData data; - if (!GetNotarisationData(proof.notarisationHeight, data, true)) - return Invalid("coudnt-load-momom"); - - if (data.MoMoM != proof.branch.Exec(burnTx.GetHash())) - return Invalid("momom-check-fail"); - } - - return Valid(); -} - - static bool UnmarshalImportTx(const CTransaction &importTx, MomoProof &proof, CTransaction &burnTx) { CScript scriptSig = importTx.vin[0].scriptSig; diff --git a/src/cc/importcoin.h b/src/importcoin.h similarity index 93% rename from src/cc/importcoin.h rename to src/importcoin.h index 13fefd513..6f712a1e9 100644 --- a/src/cc/importcoin.h +++ b/src/importcoin.h @@ -1,5 +1,5 @@ -#ifndef CC_IMPORTCOIN_H -#define CC_IMPORTCOIN_H +#ifndef IMPORTCOIN_H +#define IMPORTCOIN_H #include "cc/eval.h" #include "coins.h" @@ -36,4 +36,4 @@ void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, i void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs); int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs); -#endif /* CC_IMPORTCOIN_H */ +#endif /* IMPORTCOIN_H */ diff --git a/src/init.cpp b/src/init.cpp index aa55d1782..52c919421 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -21,6 +21,7 @@ #include "httpserver.h" #include "httprpc.h" #include "key.h" +#include "notarisationdb.h" #include "main.h" #include "metrics.h" #include "miner.h" @@ -1413,11 +1414,14 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) delete pcoinsdbview; delete pcoinscatcher; delete pblocktree; + delete pnotarisations; pblocktree = new CBlockTreeDB(nBlockTreeDBCache, false, fReindex, dbCompression, dbMaxOpenFiles); pcoinsdbview = new CCoinsViewDB(nCoinDBCache, false, fReindex); pcoinscatcher = new CCoinsViewErrorCatcher(pcoinsdbview); pcoinsTip = new CCoinsViewCache(pcoinscatcher); + pnotarisations = new NotarisationDB(100*1024*1024, false, fReindex); + if (fReindex) { pblocktree->WriteReindexing(true); diff --git a/src/main.cpp b/src/main.cpp index 67220e891..a3bd0f515 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,7 @@ #include "addrman.h" #include "alert.h" #include "arith_uint256.h" -#include "cc/importcoin.h" +#include "importcoin.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -20,6 +20,7 @@ #include "init.h" #include "merkleblock.h" #include "metrics.h" +#include "notarisationdb.h" #include "net.h" #include "pow.h" #include "script/interpreter.h" @@ -2598,6 +2599,7 @@ void PartitionCheck(bool (*initialDownloadCheck)(), CCriticalSection& cs, const } } + static int64_t nTimeVerify = 0; static int64_t nTimeConnect = 0; static int64_t nTimeIndex = 0; @@ -2833,6 +2835,14 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin vPos.push_back(std::make_pair(tx.GetHash(), pos)); pos.nTxOffset += ::GetSerializeSize(tx, SER_DISK, CLIENT_VERSION); } + + + // Record Notarisations + NotarisationsInBlock notarisations = GetNotarisationsInBlock(block, pindex->nHeight); + pnotarisations->Write(block.GetHash(), notarisations); + WriteBackNotarisations(notarisations); // Very important to disconnect this + // TODO: Disconnect? + view.PushAnchor(tree); if (!fJustCheck) { diff --git a/src/miner.cpp b/src/miner.cpp index f57b53d98..4fdca90bd 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -11,7 +11,7 @@ #include "amount.h" #include "base58.h" #include "chainparams.h" -#include "cc/importcoin.h" +#include "importcoin.h" #include "consensus/consensus.h" #include "consensus/upgrades.h" #include "consensus/validation.h" diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp new file mode 100644 index 000000000..6daa5a132 --- /dev/null +++ b/src/notarisationdb.cpp @@ -0,0 +1,46 @@ +#include "leveldbwrapper.h" +#include "notarisationdb.h" +#include "uint256.h" +#include "cc/eval.h" + +#include + + +NotarisationDB *pnotarisations; + + +NotarisationDB::NotarisationDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "notarisations", nCacheSize, fMemory, fWipe, false, 64) { +} + +NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight) +{ + EvalRef eval; + NotarisationsInBlock vNotarisations; + bool IsBackNotarisation = ASSETCHAINS_SYMBOL[0] != 0; + + for (unsigned int i = 0; i < block.vtx.size(); i++) { + CTransaction tx = block.vtx[i]; + if (eval->CheckNotaryInputs(tx, nHeight, block.nTime)) { + NotarisationData data(IsBackNotarisation); + if (ParseNotarisationOpReturn(tx, data)) + vNotarisations.push_back(std::make_pair(tx.GetHash(), data)); + else + fprintf(stderr, "Warning: Couldn't parse notarisation for tx: %s at height %i\n", + tx.GetHash().GetHex().data(), nHeight); + } + } + return vNotarisations; +} + + +/* + * Write an index of KMD notarisation id -> backnotarisation + */ +void WriteBackNotarisations(NotarisationsInBlock notarisations) +{ + BOOST_FOREACH(Notarisation &n, notarisations) + { + if (n.second.IsBackNotarisation) + pnotarisations->Write(n.second.txHash, n); + } +} diff --git a/src/notarisationdb.h b/src/notarisationdb.h new file mode 100644 index 000000000..bd2c15685 --- /dev/null +++ b/src/notarisationdb.h @@ -0,0 +1,26 @@ +#ifndef NOTARISATIONDB_H +#define NOTARISATIONDB_H + +#include "uint256.h" +#include "leveldbwrapper.h" +#include "cc/eval.h" + + +class NotarisationDB : public CLevelDBWrapper +{ +public: + NotarisationDB(size_t nCacheSize, bool fMemory = false, bool fWipe = false); +}; + + +extern NotarisationDB *pnotarisations; + + +typedef std::pair Notarisation; +typedef std::vector NotarisationsInBlock; + +NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight); + +void WriteBackNotarisations(NotarisationsInBlock notarisations); + +#endif /* NOTARISATIONDB_H */ diff --git a/src/primitives/block.cpp b/src/primitives/block.cpp index c2300c82a..106216284 100644 --- a/src/primitives/block.cpp +++ b/src/primitives/block.cpp @@ -15,7 +15,9 @@ uint256 CBlockHeader::GetHash() const return SerializeHash(*this); } -uint256 CBlock::BuildMerkleTree(bool* fMutated) const + +uint256 BuildMerkleTree(bool* fMutated, const std::vector leaves, + std::vector &vMerkleTree) { /* WARNING! If you're reading this because you're learning about crypto and/or designing a new system that will use merkle trees, keep in mind @@ -28,10 +30,10 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const transactions leading to the same merkle root. For example, these two trees: - A A - / \ / \ - B C B C - / \ | / \ / \ + A A + / \ / \ + B C B C + / \ \ / \ / \ D E F D E F F / \ / \ / \ / \ / \ / \ / \ 1 2 3 4 5 6 1 2 3 4 5 6 5 6 @@ -52,13 +54,14 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const known ways of changing the transactions without affecting the merkle root. */ + vMerkleTree.clear(); - vMerkleTree.reserve(vtx.size() * 2 + 16); // Safe upper bound for the number of total nodes. - for (std::vector::const_iterator it(vtx.begin()); it != vtx.end(); ++it) - vMerkleTree.push_back(it->GetHash()); + vMerkleTree.reserve(leaves.size() * 2 + 16); // Safe upper bound for the number of total nodes. + for (std::vector::const_iterator it(leaves.begin()); it != leaves.end(); ++it) + vMerkleTree.push_back(*it); int j = 0; bool mutated = false; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + for (int nSize = leaves.size(); nSize > 1; nSize = (nSize + 1) / 2) { for (int i = 0; i < nSize; i += 2) { @@ -78,6 +81,15 @@ uint256 CBlock::BuildMerkleTree(bool* fMutated) const return (vMerkleTree.empty() ? uint256() : vMerkleTree.back()); } + +uint256 CBlock::BuildMerkleTree(bool* fMutated) const +{ + std::vector leaves; + for (int i=0; i CBlock::GetMerkleBranch(int nIndex) const { if (vMerkleTree.empty()) diff --git a/src/primitives/block.h b/src/primitives/block.h index 6b3f13a86..2d3c0cfe4 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -139,6 +139,10 @@ public: }; +uint256 BuildMerkleTree(bool* fMutated, const std::vector leaves, + std::vector &vMerkleTree); + + /** * Custom serializer for CBlockHeader that omits the nonce and solution, for use * as input to Equihash. diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index d09479008..75454497d 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -7,6 +7,7 @@ #include "chain.h" #include "chainparams.h" #include "checkpoints.h" +#include "crosschain.h" #include "base58.h" #include "consensus/validation.h" #include "cc/eval.h" @@ -934,87 +935,35 @@ UniValue txMoMproof(const UniValue& params, bool fHelp) int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; // parse params and get notarisation data for tx - { - if ( fHelp || params.size() != 1) - throw runtime_error("txMoMproof needs a txid"); + if ( fHelp || params.size() != 1) + throw runtime_error("txMoMproof needs a txid"); - hash = uint256S(params[0].get_str()); + hash = uint256S(params[0].get_str()); - uint256 blockHash; - CTransaction tx; - if (!GetTransaction(hash, tx, blockHash, true)) - throw runtime_error("cannot find transaction"); - - blockIndex = mapBlockIndex[blockHash]; - - depth = komodo_MoM(¬arisedHeight, &MoM, ¬arisationHash, blockIndex->nHeight,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi); - - if (!depth) - throw runtime_error("notarisation not found"); - - // index of block in MoM leaves - nIndex = notarisedHeight - blockIndex->nHeight; - } - - // build merkle chain from blocks to MoM - { - // since the merkle branch code is tied up in a block class - // and we want to make a merkle branch for something that isnt transactions - CBlock fakeBlock; - for (int i=0; ihashMerkleRoot; - CTransaction fakeTx; - // first value in CTransaction memory is it's hash - memcpy((void*)&fakeTx, mRoot.begin(), 32); - fakeBlock.vtx.push_back(fakeTx); - } - branch = fakeBlock.GetMerkleBranch(nIndex); - - // Check branch - if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle block->MoM"); - } - - // Now get the tx merkle branch - { - CBlock block; - - if (fHavePruned && !(blockIndex->nStatus & BLOCK_HAVE_DATA) && blockIndex->nTx > 0) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Block not available (pruned data)"); - - if(!ReadBlockFromDisk(block, blockIndex,1)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); - - // Locate the transaction in the block - int nTxIndex; - for (nTxIndex = 0; nTxIndex < (int)block.vtx.size(); nTxIndex++) - if (block.vtx[nTxIndex].GetHash() == hash) - break; - - if (nTxIndex == (int)block.vtx.size()) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Error locating tx in block"); - - std::vector txBranch = block.GetMerkleBranch(nTxIndex); - - // Check branch - if (block.hashMerkleRoot != CBlock::CheckMerkleBranch(hash, txBranch, nTxIndex)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed merkle tx->block"); - - // concatenate branches - nIndex = (nIndex << txBranch.size()) + nTxIndex; - branch.insert(branch.begin(), txBranch.begin(), txBranch.end()); - } - - // Check the proof - if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex)) - throw JSONRPCError(RPC_INTERNAL_ERROR, "Failed validating MoM"); - - // Encode and return - CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); - ssProof << std::make_pair(notarisationHash, MerkleBranch(nIndex, branch)); - return HexStr(ssProof.begin(), ssProof.end()); + std::vector proofData = E_MARSHAL(ss << GetAssetchainProof(hash)); + return HexStr(proofData); } + +UniValue getproofroot(const UniValue& params, bool fHelp) +{ + std::string symbol; + int kmdHeight; + + + // parse params and get notarisation data for tx + if ( fHelp || params.size() != 2) + throw runtime_error("getproofroot needs a symbol and a kmdHeight"); + symbol = params[0].get_str(); + kmdHeight = atoi(params[0].get_str().c_str()); + if (kmdHeight <= 0) + throw runtime_error("Invalid kmdHeight"); + + UniValue ret(UniValue::VOBJ); + return ret; +} + + UniValue minerids(const UniValue& params, bool fHelp) { uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); uint8_t minerids[2000],pubkeys[65][33]; int32_t i,j,n,numnotaries,tally[129]; diff --git a/src/script/cc.h b/src/script/cc.h index dc019beba..246f19052 100644 --- a/src/script/cc.h +++ b/src/script/cc.h @@ -1,6 +1,8 @@ #ifndef SCRIPT_CC_H #define SCRIPT_CC_H +#include + #include "pubkey.h" #include "script/script.h" #include "cryptoconditions/include/cryptoconditions.h" @@ -79,5 +81,4 @@ bool GetPushData(const CScript &sig, std::vector &data); */ bool GetOpReturnData(const CScript &sig, std::vector &data); - #endif /* SCRIPT_CC_H */ diff --git a/src/test-komodo/main.cpp b/src/test-komodo/main.cpp index 0ce8740b3..1adb729b4 100644 --- a/src/test-komodo/main.cpp +++ b/src/test-komodo/main.cpp @@ -9,6 +9,7 @@ int main(int argc, char **argv) { assert(init_and_check_sodium() != -1); ECC_Start(); + ECCVerifyHandle handle; // Inits secp256k1 verify context SelectParams(CBaseChainParams::REGTEST); CBitcoinSecret vchSecret; diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp index 96543d80b..fe2f3ce96 100644 --- a/src/test-komodo/test_coinimport.cpp +++ b/src/test-komodo/test_coinimport.cpp @@ -2,14 +2,14 @@ #include #include -#include "cc/importcoin.h" #include "cc/eval.h" +#include "importcoin.h" #include "base58.h" #include "core_io.h" #include "key.h" #include "main.h" -#include "script/cc.h" #include "primitives/transaction.h" +#include "script/cc.h" #include "script/interpreter.h" #include "script/serverchecker.h" #include "txmempool.h" diff --git a/src/test-komodo/test_crosschain.cpp b/src/test-komodo/test_crosschain.cpp new file mode 100644 index 000000000..f7f7d47ab --- /dev/null +++ b/src/test-komodo/test_crosschain.cpp @@ -0,0 +1,267 @@ +#include +#include +#include +#include +#include + +#include +#include + +#include "cc/eval.h" +#include "importcoin.h" +#include "base58.h" +#include "core_io.h" +#include "crosschain.h" +#include "key.h" +#include "main.h" +#include "primitives/block.h" +#include "primitives/transaction.h" +#include "script/cc.h" +#include "script/interpreter.h" +#include "script/serverchecker.h" +#include "txmempool.h" +#include "crosschain.h" + +#include "testutils.h" + + +extern uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); + + +/* + * Tests for the whole process of creating and validating notary proofs + * using proof roots (MoMoMs). This is to support coin imports. + */ + +namespace TestCrossChainProof { + + +class TestCrossChain : public ::testing::Test, public Eval { +public: + bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const + { + NotarisationData data; + return ParseNotarisationOpReturn(tx, data); + } +protected: + static void SetUpTestCase() { } + virtual void SetUp() { + ASSETCHAINS_CC = 1; + EVAL_TEST = this; + } +}; + + + +TEST_F(TestCrossChain, testCreateAndValidateImportProof) +{ + /* + * This tests the full process of creation of a cross chain proof. + * For the purposes of the test we will use one assetchain and a KMD chain. + * + * In order to do this test, we need 2 blockchains, so we'll fork and make a socket + * for IPC. + */ + + int childPid = fork(); + void *ctx = zmq_ctx_new(); + void *socket = zmq_socket(ctx, ZMQ_PAIR); + setupChain(); + std::vector blocks; + blocks.resize(10); + NotarisationData a2kmd, kmd2a(true); + + + auto SendIPC = [&] (std::vector v) { + assert(v.size() == zmq_send(socket, v.data(), v.size(), 0)); + }; + + auto RecvIPC = [&] () { + std::vector out; + out.resize(100000); + int len = zmq_recv(socket, out.data(), out.size(), 0); + assert(len != -1); + out.resize(len); + return out; + }; + + auto RecordNotarisation = [&] (CTransaction inputTx, NotarisationData data) { + CMutableTransaction mtx = spendTx(inputTx); + mtx.vout.resize(2); + mtx.vout[0].scriptPubKey << VCH(notaryKey.GetPubKey().begin(), 33) << OP_CHECKSIG; + mtx.vout[1].scriptPubKey << OP_RETURN << E_MARSHAL(ss << data); + mtx.vout[1].nValue = 0; + mtx.vin[0].scriptSig << getSig(mtx, inputTx.vout[0].scriptPubKey); + + acceptTxFail(CTransaction(mtx)); + printf("accept %snotarisation: %s\n", data.IsBackNotarisation ? "back" : "", + mtx.GetHash().GetHex().data()); + return mtx.GetHash(); + }; + + auto RunTestAssetchain = [&] () + { + NotarisationData back(1); + strcpy(ASSETCHAINS_SYMBOL, "symbolA"); + strcpy(a2kmd.symbol, "symbolA"); + a2kmd.ccId = 2; + + /* + * Notarisation 1 + */ + generateBlock(&blocks[1]); + generateBlock(&blocks[2]); + a2kmd.blockHash = blocks[2].GetHash(); + a2kmd.MoM = komodo_calcMoM(a2kmd.height = chainActive.Height(), a2kmd.MoMDepth = 2); + SendIPC(E_MARSHAL(ss << a2kmd)); + E_UNMARSHAL(RecvIPC(), ss >> back); + RecordNotarisation(blocks[1].vtx[0], back); + + /* + * Notarisation 2 + */ + generateBlock(&blocks[3]); + generateBlock(&blocks[4]); + a2kmd.blockHash = blocks[4].GetHash(); + a2kmd.MoM = komodo_calcMoM(a2kmd.height = chainActive.Height(), a2kmd.MoMDepth = 2); + SendIPC(E_MARSHAL(ss << a2kmd)); + E_UNMARSHAL(RecvIPC(), ss >> back); + RecordNotarisation(blocks[3].vtx[0], back); + + /* + * Generate proof + */ + generateBlock(&blocks[5]); + uint256 txid = blocks[3].vtx[0].GetHash(); + std::pair assetChainProof = GetAssetchainProof(txid); + SendIPC(E_MARSHAL(ss << txid; ss << assetChainProof)); + }; + + auto RunTestKmd = [&] () + { + NotarisationData n; + + /* + * Notarisation 1 + */ + E_UNMARSHAL(RecvIPC(), ss >> n); + // Grab a coinbase input to fund notarisation + generateBlock(&blocks[1]); + n.txHash = RecordNotarisation(blocks[1].vtx[0], a2kmd); + n.height = chainActive.Height(); + SendIPC(E_MARSHAL(ss << n)); + + /* + * Notarisation 2 + */ + E_UNMARSHAL(RecvIPC(), ss >> n); + // Grab a coinbase input to fund notarisation + generateBlock(&blocks[2]); + n.txHash = RecordNotarisation(blocks[2].vtx[0], a2kmd); + n.height = chainActive.Height(); + SendIPC(E_MARSHAL(ss << n)); + + /* + * Extend proof + */ + std::pair assetChainProof; + uint256 txid; + // Extend proof to MoMoM + assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> kmd2a)); + std::pair ccProof = GetCrossChainProof(txid, (char*)"symbolA", + 2, assetChainProof.first, assetChainProof.second); + }; + + const char endpoint[] = "ipc://tmpKomodoTestCrossChainSock"; + + if (!childPid) { + assert(0 == zmq_connect(socket, endpoint)); + usleep(20000); + RunTestAssetchain(); + exit(0); + } + else { + assert(0 == zmq_bind(socket, endpoint)); + RunTestKmd(); + int returnStatus; + waitpid(childPid, &returnStatus, 0); + unlink("tmpKomodoTestCrossChainSock"); + ASSERT_EQ(0, returnStatus); + } + + + + /* + + * + * Assetchain notarisation 2 + * + + ON_ASSETCHAIN { + a2kmd.blockHash = blocks[4].GetHash(); + a2kmd.MoM = komodo_calcMoM(a2kmd.height = chainActive.Height(), a2kmd.MoMDepth = 2); + SendIPC(E_MARSHAL(ss << a2kmd)); + } + + ON_KMD { + assert(E_UNMARSHAL(RecvIPC(), ss >> a2kmd)); + // Grab a coinbase input to fund notarisation + RecordNotarisation(blocks[2].vtx[0], a2kmd); + } + + generateBlock(&blocks[5]); + generateBlock(&blocks[6]); + + * + * Backnotarisation + * + * This is what will contain the MoMoM which allows us to prove across chains + * + std::vector moms; + int assetChainHeight; + + ON_KMD { + memset(kmd2a.txHash.begin(), 1, 32); // Garbage but non-null + kmd2a.symbol[0] = 0; // KMD + kmd2a.MoMoM = GetProofRoot((char*)"symbolA", 2, chainActive.Height(), moms, &assetChainHeight); + kmd2a.MoMoMDepth = 0; // Needed? + SendIPC(E_MARSHAL(ss << kmd2a)); + } + + ON_ASSETCHAIN { + assert(E_UNMARSHAL(RecvIPC(), ss >> kmd2a)); + RecordNotarisation(blocks[1].vtx[0], kmd2a); + } + + + * + * We can now prove a tx from A on A, via a merkle root backpropagated from KMD. + * + * The transaction that we'll try to prove is the coinbase from the 3rd block. + * We should be able to start with only that transaction ID, and generate a merkle + * proof. + * + + std::pair assetChainProof; + uint256 txid; + + ON_ASSETCHAIN { + txid = blocks[2].vtx[0].GetHash(); + + // First thing to do is get the proof from the assetchain + assetChainProof = GetAssetchainProof(txid); + SendIPC(E_MARSHAL(ss << txid; ss << assetChainProof)); + } + + ON_KMD { + // Extend proof to MoMoM + assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> kmd2a)); + std::pair ccProof = GetCrossChainProof(txid, (char*)"symbolA", + 2, assetChainProof.first, assetChainProof.second); + } + + */ +} + + +} /* namespace TestCrossChainProof */ diff --git a/src/test-komodo/testutils.cpp b/src/test-komodo/testutils.cpp index c3baf4fb6..caf805499 100644 --- a/src/test-komodo/testutils.cpp +++ b/src/test-komodo/testutils.cpp @@ -6,6 +6,7 @@ #include "key.h" #include "main.h" #include "miner.h" +#include "notarisationdb.h" #include "random.h" #include "rpcserver.h" #include "rpcprotocol.h" @@ -33,13 +34,14 @@ CKey notaryKey; int64_t nMockTime; extern uint32_t USE_EXTERNAL_PUBKEY; +extern std::string NOTARY_PUBKEY; void setupChain() { SelectParams(CBaseChainParams::REGTEST); // Settings to get block reward - //NOTARY_PUBKEY = _NOTARY_PUBKEY; + NOTARY_PUBKEY = notaryPubkey; USE_EXTERNAL_PUBKEY = 1; mapArgs["-mineraddress"] = "bogus"; COINBASE_MATURITY = 1; @@ -54,6 +56,7 @@ void setupChain() pblocktree = new CBlockTreeDB(1 << 20, true); CCoinsViewDB *pcoinsdbview = new CCoinsViewDB(1 << 23, true); pcoinsTip = new CCoinsViewCache(pcoinsdbview); + pnotarisations = new NotarisationDB(1 << 20, true); InitBlockIndex(); } @@ -65,15 +68,19 @@ void generateBlock(CBlock *block) params.push_back(1); uint256 blockId; - SetMockTime(nMockTime++); // CreateNewBlock can fail if not enough time passes + SetMockTime(nMockTime+=100); // CreateNewBlock can fail if not enough time passes + + char symbolPrefix = ASSETCHAINS_SYMBOL[0]; + ASSETCHAINS_SYMBOL[0] = 0; // generate block fails otherwise try { UniValue out = generate(params, false); blockId.SetHex(out[0].getValStr()); + ASSETCHAINS_SYMBOL[0] = symbolPrefix; + if (block) ASSERT_TRUE(ReadBlockFromDisk(*block, mapBlockIndex[blockId], false)); } catch (const UniValue& e) { FAIL() << "failed to create block: " << e.write().data(); } - if (block) ASSERT_TRUE(ReadBlockFromDisk(*block, mapBlockIndex[blockId])); } @@ -91,7 +98,7 @@ bool acceptTx(const CTransaction tx, CValidationState &state) } -static CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0) +CMutableTransaction spendTx(const CTransaction &txIn, int nOut) { CMutableTransaction mtx; mtx.vin.resize(1); @@ -103,6 +110,16 @@ static CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0) } +std::vector getSig(const CMutableTransaction mtx, CScript inputPubKey, int nIn) +{ + uint256 hash = SignatureHash(inputPubKey, mtx, nIn, SIGHASH_ALL, 0, 0); + std::vector vchSig; + notaryKey.Sign(hash, vchSig); + vchSig.push_back((unsigned char)SIGHASH_ALL); + return vchSig; +} + + /* * In order to do tests there needs to be inputs to spend. * This method creates a block and returns a transaction that spends the coinbase. diff --git a/src/test-komodo/testutils.h b/src/test-komodo/testutils.h index 92d28b587..bbf702f26 100644 --- a/src/test-komodo/testutils.h +++ b/src/test-komodo/testutils.h @@ -22,6 +22,8 @@ void generateBlock(CBlock *block=NULL); bool acceptTx(const CTransaction tx, CValidationState &state); void acceptTxFail(const CTransaction tx); void getInputTx(CScript scriptPubKey, CTransaction &txIn); +CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0); +std::vector getSig(const CMutableTransaction mtx, CScript inputPubKey, int nIn=0); #endif /* TESTUTILS_H */