From 9bf132a5a849472c672661cc18fbc1bb6d88223b Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Fri, 6 Apr 2018 03:52:30 -0300 Subject: [PATCH] tests for getting MoM in Eval and fixes --- src/Makefile.ktest.include | 3 +- src/cc/betprotocol.cpp | 38 ---- src/cc/betprotocol.h | 8 +- src/cc/eval.cpp | 74 ++++--- src/cc/eval.h | 28 ++- src/cc/importpayout.cpp | 6 +- src/rpcblockchain.cpp | 19 +- .../{test_bet.cpp => test_eval_bet.cpp} | 19 +- src/test-komodo/test_eval_notarisation.cpp | 203 ++++++++++++++++++ 9 files changed, 307 insertions(+), 91 deletions(-) rename src/test-komodo/{test_bet.cpp => test_eval_bet.cpp} (98%) create mode 100644 src/test-komodo/test_eval_notarisation.cpp diff --git a/src/Makefile.ktest.include b/src/Makefile.ktest.include index cef80b0d1..06aab54dc 100644 --- a/src/Makefile.ktest.include +++ b/src/Makefile.ktest.include @@ -6,7 +6,8 @@ bin_PROGRAMS += komodo-test komodo_test_SOURCES = \ test-komodo/main.cpp \ test-komodo/test_cryptoconditions.cpp \ - test-komodo/test_bet.cpp + test-komodo/test_eval_bet.cpp \ + test-komodo/test_eval_notarisation.cpp komodo_test_CPPFLAGS = $(komodod_CPPFLAGS) diff --git a/src/cc/betprotocol.cpp b/src/cc/betprotocol.cpp index 92ec961ee..12637ebbd 100644 --- a/src/cc/betprotocol.cpp +++ b/src/cc/betprotocol.cpp @@ -7,44 +7,6 @@ #include "primitives/transaction.h" -static unsigned char* CopyPubKey(CPubKey pkIn) -{ - unsigned char* pk = (unsigned char*) malloc(33); - memcpy(pk, pkIn.begin(), 33); // TODO: compressed? - return pk; -} - - -CC* CCNewThreshold(int t, std::vector v) -{ - CC *cond = cc_new(CC_Threshold); - cond->threshold = t; - cond->size = v.size(); - cond->subconditions = (CC**) calloc(v.size(), sizeof(CC*)); - memcpy(cond->subconditions, v.data(), v.size() * sizeof(CC*)); - return cond; -} - - -CC* CCNewSecp256k1(CPubKey k) -{ - CC *cond = cc_new(CC_Secp256k1); - cond->publicKey = CopyPubKey(k); - return cond; -} - - -CC* CCNewEval(std::string method, std::vector paramsBin) -{ - CC *cond = cc_new(CC_Eval); - strcpy(cond->method, method.data()); - cond->paramsBin = (unsigned char*) malloc(paramsBin.size()); - memcpy(cond->paramsBin, paramsBin.data(), paramsBin.size()); - cond->paramsBinLength = paramsBin.size(); - return cond; -} - - std::vector BetProtocol::PlayerConditions() { std::vector subs; diff --git a/src/cc/betprotocol.h b/src/cc/betprotocol.h index dcbb6ec4e..67a1979c3 100644 --- a/src/cc/betprotocol.h +++ b/src/cc/betprotocol.h @@ -2,13 +2,11 @@ #define BETPROTOCOL_H #include "pubkey.h" +#include "primitives/block.h" #include "primitives/transaction.h" #include "cryptoconditions/include/cryptoconditions.h" -#define ExecMerkle CBlock::CheckMerkleBranch - - class MoMProof { public: @@ -18,6 +16,7 @@ public: MoMProof() {} MoMProof(int i, std::vector b, uint256 n) : notarisationHash(n), nIndex(i), branch(b) {} + uint256 Exec(uint256 hash) const { return CBlock::CheckMerkleBranch(hash, branch, nIndex); } ADD_SERIALIZE_METHODS; @@ -79,7 +78,4 @@ public: }; -CC* CCNewSecp256k1(CPubKey k); - - #endif /* BETPROTOCOL_H */ diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 589a362c0..3c605e1af 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -88,14 +88,19 @@ bool Eval::GetBlock(uint256 hash, CBlockIndex& blockIdx) const extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); +int32_t Eval::GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const +{ + return komodo_notaries(pubkeys, height, timestamp); +} + + bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const { if (tx.vin.size() < 11) return false; + uint8_t seenNotaries[64] = {0}; uint8_t notaries[64][33]; - uint8_t seenNotaries[64]; - int nNotaries = komodo_notaries(notaries, height, timestamp); - char pk[33]; + int nNotaries = GetNotaries(notaries, height, timestamp); BOOST_FOREACH(const CTxIn &txIn, tx.vin) { @@ -104,10 +109,11 @@ bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t t uint256 hashBlock; if (!GetTx(txIn.prevout.hash, tx, hashBlock, false)) return false; if (tx.vout.size() < txIn.prevout.n) return false; - const unsigned char *script = tx.vout[txIn.prevout.n].scriptPubKey.data(); - if (script[0] != 33) return false; - memcpy(pk, script+1, 33); - return true; + CScript spk = tx.vout[txIn.prevout.n].scriptPubKey; + if (spk.size() != 35) return false; + const unsigned char *pk = spk.data(); + if (pk++[0] != 33) return false; + if (pk[33] != OP_CHECKSIG) return false; // Check it's a notary for (int i=0; i vdata; + if (!GetOpReturnData(scriptPK, vdata)) return false; + + CDataStream ss(vdata, SER_NETWORK, PROTOCOL_VERSION); + + try { + ss >> blockHash; + ss >> height; + + 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; +} + + + /* * Get MoM from a notarisation tx hash */ -bool Eval::GetMoM(const uint256 notaryHash, uint256 &mom) const +bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) const { CTransaction notarisationTx; uint256 notarisationBlock; - if (!GetTx(notaryHash, notarisationTx, notarisationBlock, true)) return 0; + if (!GetTx(notaryHash, notarisationTx, notarisationBlock, true)) return false; CBlockIndex block; - if (!GetBlock(notarisationBlock, block)) return 0; - if (!CheckNotaryInputs(notarisationTx, block.nHeight, block.nTime)) { - return false; - } - if (!notarisationTx.vout.size() < 1) return 0; - std::vector opret; - if (!GetOpReturnData(notarisationTx.vout[0].scriptPubKey, opret)) return 0; - if (opret.size() < 36) return 0; // In reality it is more than 36, but at the moment I - // only know where it is relative to the end, and this - // is enough to prevent a memory fault. In the case that - // the assumption about the presence of a MoM at this - // offset fails, we will return random other data that is - // not more likely to generate a false positive. - memcpy(mom.begin(), opret.data()+opret.size()-36, 32); - return 1; + if (!GetBlock(notarisationBlock, 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; + return true; } diff --git a/src/cc/eval.h b/src/cc/eval.h index 2060d76ca..55d45153c 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -11,6 +11,7 @@ class AppVM; +class NotarisationData; class Eval @@ -44,7 +45,8 @@ public: virtual unsigned int GetCurrentHeight() const; virtual bool GetSpends(uint256 hash, std::vector &spends) const; virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const; - virtual bool GetMoM(uint256 notarisationHash, uint256& MoM) const; + virtual int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const; + virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const; virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const; }; @@ -69,16 +71,25 @@ public: }; +/* + * Data from notarisation OP_RETURN + */ +class NotarisationData { +public: + uint256 blockHash; + uint32_t height; + uint256 txHash; + char symbol[64]; + uint256 MoM; + uint32_t MoMDepth; + + bool Parse(CScript scriptPubKey); +}; + + /* * Serialisation boilerplate */ -template -std::vector CheckSerialize(T &in); -template -bool CheckDeserialize(std::vector vIn, T &out); - - - template std::vector CheckSerialize(T &in) { @@ -87,7 +98,6 @@ std::vector CheckSerialize(T &in) return std::vector(ss.begin(), ss.end()); } - template bool CheckDeserialize(std::vector vIn, T &out) { diff --git a/src/cc/importpayout.cpp b/src/cc/importpayout.cpp index 5251f1dfd..3be0cdaf7 100644 --- a/src/cc/importpayout.cpp +++ b/src/cc/importpayout.cpp @@ -69,10 +69,10 @@ bool Eval::ImportPayout(const CC *cond, const CTransaction &payoutTx, unsigned i if (!CheckDeserialize(vProof, proof)) return Invalid("invalid-mom-proof-payload"); - uint256 MoM; - if (!GetMoM(proof.notarisationHash, MoM)) return Invalid("coudnt-load-mom"); + NotarisationData data; + if (!GetNotarisationData(proof.notarisationHash, data)) return Invalid("coudnt-load-mom"); - if (MoM != ExecMerkle(disputeTx.GetHash(), proof.branch, proof.nIndex)) + if (data.MoM != proof.Exec(disputeTx.GetHash())) return Invalid("mom-check-fail"); } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index f353d549d..343b651f0 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -635,7 +635,7 @@ UniValue txMoMproof(const UniValue& params, bool fHelp) if ( fHelp || params.size() != 1) throw runtime_error("txmomproof needs a txid"); - uint256 hash(uint256S(params[0].get_str())); + hash = uint256S(params[0].get_str()); uint256 blockHash; CTransaction tx; @@ -666,6 +666,10 @@ UniValue txMoMproof(const UniValue& params, bool fHelp) 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 @@ -687,12 +691,21 @@ UniValue txMoMproof(const UniValue& params, bool fHelp) if (nTxIndex == (int)block.vtx.size()) throw JSONRPCError(RPC_INTERNAL_ERROR, "Error locating tx in block"); - // concatenate branches std::vector txBranch = block.GetMerkleBranch(nTxIndex); - nIndex = nIndex << txBranch.size() + 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 << MoMProof(nIndex, branch, notarisationHash); diff --git a/src/test-komodo/test_bet.cpp b/src/test-komodo/test_eval_bet.cpp similarity index 98% rename from src/test-komodo/test_bet.cpp rename to src/test-komodo/test_eval_bet.cpp index d0072ad38..25fea5d84 100644 --- a/src/test-komodo/test_bet.cpp +++ b/src/test-komodo/test_eval_bet.cpp @@ -14,6 +14,11 @@ #include "testutils.h" +extern Eval* EVAL_TEST; + + +namespace TestBet { + static std::vector playerSecrets; static std::vector players; @@ -72,7 +77,7 @@ public: std::map blocks; std::map> spends; - bool Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) + bool Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) { if (strcmp(cond->method, "DisputeBet") == 0) { MockVM vm; @@ -115,10 +120,10 @@ public: return true; } - bool GetMoM(uint256 notarisationHash, uint256& _MoM) const + bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const { if (notarisationHash == NotarisationHash()) { - _MoM = MoM; + data.MoM = MoM; return true; } return false; @@ -133,9 +138,6 @@ public: }; -extern Eval* EVAL_TEST; - - /* * Generates example data that we will test with and shows how to call BetProtocol. */ @@ -230,7 +232,7 @@ public: eval.currentHeight = currentHeight; MoMProof proof = GetMoMProof(); - eval.MoM = ExecMerkle(DisputeTx(Player2).GetHash(), proof.branch, proof.nIndex); + eval.MoM = proof.Exec(DisputeTx(Player2).GetHash()); EVAL_TEST = &eval; return eval; @@ -594,3 +596,6 @@ TEST_F(TestBet, testImportPayoutMomFail) EXPECT_FALSE(TestCC(importTx, 0, payoutCond)); EXPECT_EQ("mom-check-fail", eval.state.GetRejectReason()); } + + +} /* namespace TestBet */ diff --git a/src/test-komodo/test_eval_notarisation.cpp b/src/test-komodo/test_eval_notarisation.cpp new file mode 100644 index 000000000..736e28931 --- /dev/null +++ b/src/test-komodo/test_eval_notarisation.cpp @@ -0,0 +1,203 @@ +#include +#include + +#include "cc/betprotocol.h" +#include "cc/eval.h" +#include "base58.h" +#include "core_io.h" +#include "key.h" +#include "main.h" +#include "komodo_cc.h" +#include "primitives/transaction.h" +#include "script/interpreter.h" +#include "script/serverchecker.h" + +#include "testutils.h" + + +extern Eval* EVAL_TEST; +extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); + + +namespace TestEvalNotarisation { + + +class EvalMock : public Eval +{ +public: + uint32_t nNotaries; + uint8_t notaries[64][33]; + std::map txs; + std::map blocks; + + int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const + { + memcpy(pubkeys, notaries, sizeof(notaries)); + return nNotaries; + } + + bool GetTx(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock, bool fAllowSlow) const + { + auto r = txs.find(hash); + if (r != txs.end()) { + txOut = r->second; + hashBlock = hash; + return true; + } + return false; + } + + bool GetBlock(uint256 hash, CBlockIndex& blockIdx) const + { + auto r = blocks.find(hash); + if (r == blocks.end()) return false; + blockIdx = r->second; + return true; + } +}; + +static auto noop = [&](CMutableTransaction &mtx){}; + + +template +void SetEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify) +{ + eval.nNotaries = komodo_notaries(eval.notaries, 780060, 1522946781); + + // make fake notary inputs + notary.vin.resize(11); + for (int i=0; i