From 0cb91a8d209db6d64578df7bed760994523c18c0 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Mon, 16 Apr 2018 22:57:35 -0500 Subject: [PATCH 01/38] wip --- migratecoin.md | 61 ++++++ src/Makefile.am | 5 +- src/Makefile.ktest.include | 2 + src/cc/betprotocol.cpp | 146 +++++++++++++++ src/cc/betprotocol.h | 12 +- src/cc/disputepayout.cpp | 84 --------- src/cc/eval.cpp | 37 ++++ src/cc/eval.h | 43 ++++- src/cc/importcoin.cpp | 147 +++++++++++++++ src/cc/importcoin.h | 33 ++++ src/cc/importpayout.cpp | 76 -------- src/chainparams.cpp | 16 +- src/coins.cpp | 11 +- src/coins.h | 5 + src/main.cpp | 91 +++++---- src/miner.cpp | 86 +++++---- src/primitives/transaction.h | 10 + src/rpcblockchain.cpp | 4 +- src/script/interpreter.cpp | 5 +- src/script/interpreter.h | 1 - src/script/script.cpp | 13 ++ src/script/script.h | 1 + src/test-komodo/main.cpp | 7 + src/test-komodo/test_coinimport.cpp | 206 +++++++++++++++++++++ src/test-komodo/test_cryptoconditions.cpp | 10 - src/test-komodo/test_eval_bet.cpp | 6 +- src/test-komodo/test_eval_notarisation.cpp | 24 ++- src/test-komodo/testutils.cpp | 129 +++++++++++++ src/test-komodo/testutils.h | 14 +- src/txmempool.cpp | 6 +- 30 files changed, 1002 insertions(+), 289 deletions(-) create mode 100644 migratecoin.md delete mode 100644 src/cc/disputepayout.cpp create mode 100644 src/cc/importcoin.cpp create mode 100644 src/cc/importcoin.h delete mode 100644 src/cc/importpayout.cpp create mode 100644 src/test-komodo/test_coinimport.cpp create mode 100644 src/test-komodo/testutils.cpp diff --git a/migratecoin.md b/migratecoin.md new file mode 100644 index 000000000..7859bdff2 --- /dev/null +++ b/migratecoin.md @@ -0,0 +1,61 @@ +# MigrateCoin protocol + + + +## ExportCoins tx: + + + +``` + +vin: + + [ any ] + +vout: + + - amount: {burnAmount} + + script: OP_RETURN "send to ledger {id} {voutsHash}" + +``` + + + +* ExportCoin is a standard tx which burns coins in an OP_RETURN + + + +## ImportCoins tx: + + + +``` + +vin: + + - txid: 0000000000000000000000000000000000000000000000000000000000000000 + + idx: 0 + + script: CC_EVAL(EVAL_IMPORTCOINS, {momoProof},{exportCoin}) OP_CHECKCRYPTOCONDITION_UNILATERAL + +vout: + + - [ vouts matching voutsHash in exportCoin ] + +``` + + + +* ImportCoin transaction has no signature + +* ImportCoin is non malleable + +* ImportCoin satisfies tx.IsCoinBase() + +* ImportCoin uses a new opcode which allows a one sided check (no scriptPubKey) + +* ImportCoin must contain CC opcode EVAL_IMPORTCOINS + +* ImportCoin fees are equal to the difference between burnAmount in exportCoins and the sum of outputs. diff --git a/src/Makefile.am b/src/Makefile.am index a1232968d..231386c96 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -256,8 +256,7 @@ libbitcoin_server_a_SOURCES = \ asyncrpcqueue.cpp \ bloom.cpp \ cc/eval.cpp \ - cc/importpayout.cpp \ - cc/disputepayout.cpp \ + cc/importcoin.cpp \ cc/betprotocol.cpp \ chain.cpp \ checkpoints.cpp \ @@ -605,7 +604,7 @@ endif libzcashconsensus_la_LDFLAGS = $(AM_LDFLAGS) -no-undefined $(RELDFLAGS) libzcashconsensus_la_LIBADD = $(LIBSECP256K1) -libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -DBUILD_BITCOIN_INTERNAL +libzcashconsensus_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(builddir)/obj -I$(srcdir)/secp256k1/include -I$(srcdir)/cryptoconditions/include -DBUILD_BITCOIN_INTERNAL libzcashconsensus_la_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) endif diff --git a/src/Makefile.ktest.include b/src/Makefile.ktest.include index 06aab54dc..a6d2d1829 100644 --- a/src/Makefile.ktest.include +++ b/src/Makefile.ktest.include @@ -5,7 +5,9 @@ bin_PROGRAMS += komodo-test # tool for generating our public parameters komodo_test_SOURCES = \ test-komodo/main.cpp \ + test-komodo/testutils.cpp \ test-komodo/test_cryptoconditions.cpp \ + test-komodo/test_coinimport.cpp \ test-komodo/test_eval_bet.cpp \ test-komodo/test_eval_notarisation.cpp diff --git a/src/cc/betprotocol.cpp b/src/cc/betprotocol.cpp index 53b79176c..c3be8be8f 100644 --- a/src/cc/betprotocol.cpp +++ b/src/cc/betprotocol.cpp @@ -1,5 +1,8 @@ #include +#include "hash.h" +#include "main.h" +#include "chain.h" #include "streams.h" #include "script/cc.h" #include "cc/eval.h" @@ -137,3 +140,146 @@ bool GetOpReturnHash(CScript script, uint256 &hash) hash = uint256(vHash); return true; } + + +/* + * Crypto-Condition EVAL method that verifies a payout against a transaction + * notarised on another chain. + * + * IN: params - condition params + * IN: importTx - Payout transaction on value chain (KMD) + * IN: nIn - index of input of stake + * + * importTx: Spends stakeTx with payouts from asset chain + * + * in 0: Spends Stake TX and contains ImportPayout CC + * out 0: OP_RETURN MomProof, disputeTx + * out 1-: arbitrary payouts + * + * disputeTx: Spends sessionTx.0 (opener on asset chain) + * + * in 0: spends sessionTx.0 + * in 1-: anything + * out 0: OP_RETURN hash of payouts + * out 1-: anything + */ +bool Eval::ImportPayout(const std::vector params, const CTransaction &importTx, unsigned int nIn) +{ + if (importTx.vout.size() == 0) return Invalid("no-vouts"); + + // load data from vout[0] + MoMProof proof; + CTransaction disputeTx; + { + std::vector vopret; + GetOpReturnData(importTx.vout[0].scriptPubKey, vopret); + if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx)) + return Invalid("invalid-payload"); + } + + // Check disputeTx.0 shows correct payouts + { + uint256 givenPayoutsHash; + GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash); + std::vector payouts(importTx.vout.begin() + 1, importTx.vout.end()); + if (givenPayoutsHash != SerializeHash(payouts)) + return Invalid("wrong-payouts"); + } + + // Check disputeTx spends sessionTx.0 + // condition ImportPayout params is session ID from other chain + { + uint256 sessionHash; + if (!E_UNMARSHAL(params, ss >> sessionHash)) + return Invalid("malformed-params"); + if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0)) + return Invalid("wrong-session"); + } + + // Check disputeTx solves momproof from vout[0] + { + NotarisationData data; + if (!GetNotarisationData(proof.notarisationHash, data)) + return Invalid("coudnt-load-mom"); + + if (data.MoM != proof.branch.Exec(disputeTx.GetHash())) + return Invalid("mom-check-fail"); + } + + return Valid(); +} + + +/* + * Crypto-Condition EVAL method that resolves a dispute of a session + * + * IN: vm - AppVM virtual machine to verify states + * IN: params - condition params + * IN: disputeTx - transaction attempting to resolve dispute + * IN: nIn - index of input of dispute tx + * + * disputeTx: attempt to resolve a dispute + * + * in 0: Spends Session TX first output, reveals DisputeHeader + * out 0: OP_RETURN hash of payouts + */ +bool Eval::DisputePayout(AppVM &vm, std::vector params, const CTransaction &disputeTx, unsigned int nIn) +{ + if (disputeTx.vout.size() == 0) return Invalid("no-vouts"); + + // get payouts hash + uint256 payoutHash; + if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash)) + return Invalid("invalid-payout-hash"); + + // load params + uint16_t waitBlocks; + std::vector vmParams; + if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams)) + return Invalid("malformed-params"); + + // ensure that enough time has passed + { + CTransaction sessionTx; + CBlockIndex sessionBlock; + + // if unconformed its too soon + if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock)) + return Error("couldnt-get-parent"); + + if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks) + return Invalid("dispute-too-soon"); // Not yet + } + + // get spends + std::vector spends; + if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends)) + return Error("couldnt-get-spends"); + + // verify result from VM + int maxLength = -1; + uint256 bestPayout; + for (int i=1; i vmState; + if (spends[i].vout.size() == 0) continue; + if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue; + auto out = vm.evaluate(vmParams, vmState); + uint256 resultHash = SerializeHash(out.second); + if (out.first > maxLength) { + maxLength = out.first; + bestPayout = resultHash; + } + // The below means that if for any reason there is a draw, the first dispute wins + else if (out.first == maxLength) { + if (bestPayout != payoutHash) { + fprintf(stderr, "WARNING: VM has multiple solutions of same length\n"); + bestPayout = resultHash; + } + } + } + + if (maxLength == -1) return Invalid("no-evidence"); + + return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout"); +} diff --git a/src/cc/betprotocol.h b/src/cc/betprotocol.h index b08783b85..fad33f7ed 100644 --- a/src/cc/betprotocol.h +++ b/src/cc/betprotocol.h @@ -3,7 +3,6 @@ #include "cc/eval.h" #include "pubkey.h" -#include "primitives/block.h" #include "primitives/transaction.h" #include "cryptoconditions/include/cryptoconditions.h" @@ -11,25 +10,18 @@ class MoMProof { public: - int nIndex; - std::vector branch; + MerkleBranch branch; uint256 notarisationHash; - - 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; - template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(VARINT(nIndex)); READWRITE(branch); READWRITE(notarisationHash); } }; + class BetProtocol { protected: diff --git a/src/cc/disputepayout.cpp b/src/cc/disputepayout.cpp deleted file mode 100644 index 610342274..000000000 --- a/src/cc/disputepayout.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include - -#include "hash.h" -#include "chain.h" -#include "version.h" -#include "script/cc.h" -#include "cc/eval.h" -#include "cc/betprotocol.h" -#include "primitives/transaction.h" - - -/* - * Crypto-Condition EVAL method that resolves a dispute of a session - * - * IN: vm - AppVM virtual machine to verify states - * IN: params - condition params - * IN: disputeTx - transaction attempting to resolve dispute - * IN: nIn - index of input of dispute tx - * - * disputeTx: attempt to resolve a dispute - * - * in 0: Spends Session TX first output, reveals DisputeHeader - * out 0: OP_RETURN hash of payouts - */ -bool Eval::DisputePayout(AppVM &vm, std::vector params, const CTransaction &disputeTx, unsigned int nIn) -{ - if (disputeTx.vout.size() == 0) return Invalid("no-vouts"); - - // get payouts hash - uint256 payoutHash; - if (!GetOpReturnHash(disputeTx.vout[0].scriptPubKey, payoutHash)) - return Invalid("invalid-payout-hash"); - - // load params - uint16_t waitBlocks; - std::vector vmParams; - if (!E_UNMARSHAL(params, ss >> VARINT(waitBlocks); ss >> vmParams)) - return Invalid("malformed-params"); - - // ensure that enough time has passed - { - CTransaction sessionTx; - CBlockIndex sessionBlock; - - // if unconformed its too soon - if (!GetTxConfirmed(disputeTx.vin[0].prevout.hash, sessionTx, sessionBlock)) - return Error("couldnt-get-parent"); - - if (GetCurrentHeight() < sessionBlock.nHeight + waitBlocks) - return Invalid("dispute-too-soon"); // Not yet - } - - // get spends - std::vector spends; - if (!GetSpendsConfirmed(disputeTx.vin[0].prevout.hash, spends)) - return Error("couldnt-get-spends"); - - // verify result from VM - int maxLength = -1; - uint256 bestPayout; - for (int i=1; i vmState; - if (spends[i].vout.size() == 0) continue; - if (!GetOpReturnData(spends[i].vout[0].scriptPubKey, vmState)) continue; - auto out = vm.evaluate(vmParams, vmState); - uint256 resultHash = SerializeHash(out.second); - if (out.first > maxLength) { - maxLength = out.first; - bestPayout = resultHash; - } - // The below means that if for any reason there is a draw, the first dispute wins - else if (out.first == maxLength) { - if (bestPayout != payoutHash) { - fprintf(stderr, "WARNING: VM has multiple solutions of same length\n"); - bestPayout = resultHash; - } - } - } - - if (maxLength == -1) return Invalid("no-evidence"); - - return bestPayout == payoutHash ? Valid() : Invalid("wrong-payout"); -} diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 3c53f9866..e98b0dc6b 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -49,6 +49,10 @@ bool Eval::Dispatch(const CC *cond, const CTransaction &txTo, unsigned int nIn) return ImportPayout(vparams, txTo, nIn); } + if (ecode == EVAL_IMPORTCOIN) { + return ImportCoin(vparams, txTo, nIn); + } + return Invalid("invalid-code"); } @@ -143,6 +147,12 @@ bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t t } +uint32_t Eval::GetCurrentLedgerID() const +{ + return -1; // TODO +} + + /* * Get MoM from a notarisation tx hash */ @@ -157,6 +167,11 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) return true; } +bool Eval::GetNotarisationData(int notarisationHeight, NotarisationData &data, bool verifyCanonical) const +{ + return false; +} + /* * Notarisation data, ie, OP_RETURN payload in notarisation transactions @@ -202,4 +217,26 @@ std::string EvalToStr(EvalCode c) char s[10]; sprintf(s, "0x%x", c); return std::string(s); + +} + + +uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) +{ + if (nIndex == -1) + return uint256(); + for (auto it(vMerkleBranch.begin()); it != vMerkleBranch.end(); ++it) + { + if (nIndex & 1) { + if (*it == hash) { + // non canonical. hash may be equal to node but never on the right. + return uint256(); + } + hash = Hash(BEGIN(*it), END(*it), BEGIN(hash), END(hash)); + } + else + hash = Hash(BEGIN(hash), END(hash), BEGIN(*it), END(*it)); + nIndex >>= 1; + } + return hash; } diff --git a/src/cc/eval.h b/src/cc/eval.h index f998c9f3d..c65e5f863 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -20,8 +20,10 @@ * a possible code is EVAL_BITCOIN_SCRIPT, where the entire binary * after the code is interpreted as a bitcoin script. */ -#define FOREACH_EVAL(EVAL) \ - EVAL(EVAL_IMPORTPAYOUT, 0xe1) +#define FOREACH_EVAL(EVAL) \ + EVAL(EVAL_IMPORTPAYOUT, 0xe1) \ + EVAL(EVAL_IMPORTCOIN, 0xe2) + typedef uint8_t EvalCode; @@ -54,6 +56,11 @@ public: */ bool ImportPayout(std::vector params, const CTransaction &importTx, unsigned int nIn); + /* + * Import coin from another chain with same symbol + */ + bool ImportCoin(std::vector params, const CTransaction &importTx, unsigned int nIn); + /* * IO functions */ @@ -64,7 +71,10 @@ public: virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) 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 GetNotarisationData(int notarisationHeight, NotarisationData &data, + bool verifyCanonical) const; virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const; + virtual uint32_t GetCurrentLedgerID() const; }; @@ -87,7 +97,6 @@ public: evaluate(std::vector header, std::vector body) = 0; }; - /* * Data from notarisation OP_RETURN */ @@ -99,6 +108,8 @@ public: char symbol[64]; uint256 MoM; uint32_t MoMDepth; + uint256 MoMoM; + uint32_t MoMoMDepth; bool Parse(CScript scriptPubKey); }; @@ -140,4 +151,30 @@ bool DeserializeF(const std::vector vIn, T f) } +/* + * Merkle stuff + */ +uint256 SafeCheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex); + + +class MerkleBranch +{ +public: + int nIndex; + std::vector branch; + + MerkleBranch() {} + MerkleBranch(int i, std::vector b) : nIndex(i), branch(b) {} + uint256 Exec(uint256 hash) const { return SafeCheckMerkleBranch(hash, branch, nIndex); } + + ADD_SERIALIZE_METHODS; + + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(VARINT(nIndex)); + READWRITE(branch); + } +}; + + #endif /* CC_EVAL_H */ diff --git a/src/cc/importcoin.cpp b/src/cc/importcoin.cpp new file mode 100644 index 000000000..4c9331e73 --- /dev/null +++ b/src/cc/importcoin.cpp @@ -0,0 +1,147 @@ +#include "cc/importcoin.h" +#include "hash.h" +#include "script/cc.h" +#include "primitives/transaction.h" + + +/* + * Generate ImportCoin transaction. + * + * Contains an empty OP_RETURN as first output; this is critical for preventing a double + * import. If it doesn't contain this it's invalid. The empty OP_RETURN will hang around + * in the UTXO set and the transaction will be detected as a duplicate. + */ +CTransaction MakeImportCoinTransaction(const MomoProof proof, const CTransaction burnTx, const std::vector payouts) +{ + std::vector payload = + E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx); + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].scriptSig << payload; + mtx.vin[0].prevout.n = 10e8; + mtx.vout = payouts; + return CTransaction(mtx); +} + +CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector payouts) +{ + std::vector opret = E_MARSHAL(ss << VARINT(targetChain); ss << SerializeHash(payouts)); + return CTxOut(value, CScript() << OP_RETURN << opret); +} + + +/* + * 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(); +} + + +/* + * Required by main + */ +CAmount GetCoinImportValue(const CTransaction &tx) +{ + CScript scriptSig = tx.vin[0].scriptSig; + auto pc = scriptSig.begin(); + opcodetype opcode; + std::vector evalScript; + if (!scriptSig.GetOp(pc, opcode, evalScript)) + return false; + if (pc != scriptSig.end()) + return false; + EvalCode code; + MomoProof proof; + CTransaction burnTx; + if (!E_UNMARSHAL(evalScript, ss >> proof; ss >> burnTx)) + return 0; + return burnTx.vout.size() ? burnTx.vout[0].nValue : 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. + */ +bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state) +{ + auto pc = scriptSig.begin(); + opcodetype opcode; + std::vector evalScript; + + auto f = [&] () { + if (!scriptSig.GetOp(pc, opcode, evalScript)) + return false; + if (pc != scriptSig.end()) + return false; + if (evalScript.size() == 0) + return false; + if (evalScript.begin()[0] != EVAL_IMPORTCOIN) + return false; + // Ok, all looks good so far... + CC *cond = CCNewEval(evalScript); + bool out = checker.CheckEvalCondition(cond); + cc_free(cond); + return out; + }; + + return f() ? true : state.Invalid(false, 0, "invalid-coin-import"); +} diff --git a/src/cc/importcoin.h b/src/cc/importcoin.h new file mode 100644 index 000000000..8773b09a7 --- /dev/null +++ b/src/cc/importcoin.h @@ -0,0 +1,33 @@ +#ifndef CC_IMPORTCOIN_H +#define CC_IMPORTCOIN_H + +#include "cc/eval.h" +#include "primitives/transaction.h" +#include "script/interpreter.h" +#include + + +class MomoProof +{ +public: + MerkleBranch branch; + int notarisationHeight; + ADD_SERIALIZE_METHODS; + template + inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + READWRITE(branch); + READWRITE(notarisationHeight); + } +}; + +CAmount GetCoinImportValue(const CTransaction &tx); + +CTransaction MakeImportCoinTransaction(const MomoProof proof, + const CTransaction burnTx, const std::vector payouts); + +CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector payouts); + +bool VerifyCoinImport(const CScript& scriptSig, + TransactionSignatureChecker& checker, CValidationState &state); + +#endif /* CC_IMPORTCOIN_H */ diff --git a/src/cc/importpayout.cpp b/src/cc/importpayout.cpp deleted file mode 100644 index 1363eb924..000000000 --- a/src/cc/importpayout.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include - -#include "main.h" -#include "chain.h" -#include "streams.h" -#include "cc/eval.h" -#include "cc/betprotocol.h" -#include "primitives/transaction.h" - - -/* - * Crypto-Condition EVAL method that verifies a payout against a transaction - * notarised on another chain. - * - * IN: params - condition params - * IN: importTx - Payout transaction on value chain (KMD) - * IN: nIn - index of input of stake - * - * importTx: Spends stakeTx with payouts from asset chain - * - * in 0: Spends Stake TX and contains ImportPayout CC - * out 0: OP_RETURN MomProof, disputeTx - * out 1-: arbitrary payouts - * - * disputeTx: Spends sessionTx.0 (opener on asset chain) - * - * in 0: spends sessionTx.0 - * in 1-: anything - * out 0: OP_RETURN hash of payouts - * out 1-: anything - */ -bool Eval::ImportPayout(const std::vector params, const CTransaction &importTx, unsigned int nIn) -{ - if (importTx.vout.size() == 0) return Invalid("no-vouts"); - - // load data from vout[0] - MoMProof proof; - CTransaction disputeTx; - { - std::vector vopret; - GetOpReturnData(importTx.vout[0].scriptPubKey, vopret); - if (!E_UNMARSHAL(vopret, ss >> proof; ss >> disputeTx)) - return Invalid("invalid-payload"); - } - - // Check disputeTx.0 shows correct payouts - { - uint256 givenPayoutsHash; - GetOpReturnHash(disputeTx.vout[0].scriptPubKey, givenPayoutsHash); - std::vector payouts(importTx.vout.begin() + 1, importTx.vout.end()); - if (givenPayoutsHash != SerializeHash(payouts)) - return Invalid("wrong-payouts"); - } - - // Check disputeTx spends sessionTx.0 - // condition ImportPayout params is session ID from other chain - { - uint256 sessionHash; - if (!E_UNMARSHAL(params, ss >> sessionHash)) - return Invalid("malformed-params"); - if (disputeTx.vin[0].prevout != COutPoint(sessionHash, 0)) - return Invalid("wrong-session"); - } - - // Check disputeTx solves momproof from vout[0] - { - NotarisationData data; - if (!GetNotarisationData(proof.notarisationHash, data)) - return Invalid("coudnt-load-mom"); - - if (data.MoM != proof.Exec(disputeTx.GetHash())) - return Invalid("mom-check-fail"); - } - - return Valid(); -} diff --git a/src/chainparams.cpp b/src/chainparams.cpp index df258d782..cd8c2b451 100644 --- a/src/chainparams.cpp +++ b/src/chainparams.cpp @@ -532,11 +532,19 @@ public: BOOST_STATIC_ASSERT(equihash_parameters_acceptable(N, K)); nEquihashN = N; nEquihashK = K; - genesis.nTime = 1296688602; - genesis.nBits = KOMODO_MINDIFF_NBITS; - genesis.nNonce = uint256S("0x0000000000000000000000000000000000000000000000000000000000000021"); - genesis.nSolution = ParseHex("0f2a976db4c4263da10fd5d38eb1790469cf19bdb4bf93450e09a72fdff17a3454326399"); + + + genesis = CreateGenesisBlock( + 1296688602, + uint256S("0x0000000000000000000000000000000000000000000000000000000000000009"), + ParseHex("01936b7db1eb4ac39f151b8704642d0a8bda13ec547d54cd5e43ba142fc6d8877cab07b3"), + 0x200f0f0f, 4, 0); + consensus.hashGenesisBlock = genesis.GetHash(); + + assert(consensus.hashGenesisBlock == uint256S("0x029f11d80ef9765602235e1bc9727e3eb6ba20839319f761fee920d63401e327")); + assert(genesis.hashMerkleRoot == uint256S("0xc4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb")); + nDefaultPort = 17779; nPruneAfterHeight = 1000; diff --git a/src/coins.cpp b/src/coins.cpp index bb40af9cc..b8f5ffe2c 100644 --- a/src/coins.cpp +++ b/src/coins.cpp @@ -9,6 +9,7 @@ #include "version.h" #include "policy/fees.h" #include "komodo_defs.h" +#include "cc/importcoin.h" #include @@ -392,11 +393,13 @@ extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTransaction& tx,uint32_t tiptime) const { + CAmount value,nResult = 0; if ( interestp != 0 ) *interestp = 0; + if ( tx.IsCoinImport() ) + return GetCoinImportValue(tx); if ( tx.IsCoinBase() != 0 ) return 0; - CAmount value,nResult = 0; for (unsigned int i = 0; i < tx.vin.size(); i++) { value = GetOutputFor(tx.vin[i]).nValue; @@ -421,6 +424,7 @@ CAmount CCoinsViewCache::GetValueIn(int32_t nHeight,int64_t *interestp,const CTr return nResult; } + bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const { boost::unordered_map intermediates; @@ -457,7 +461,7 @@ bool CCoinsViewCache::HaveJoinSplitRequirements(const CTransaction& tx) const bool CCoinsViewCache::HaveInputs(const CTransaction& tx) const { - if (!tx.IsCoinBase()) { + if (!tx.IsMint()) { for (unsigned int i = 0; i < tx.vin.size(); i++) { const COutPoint &prevout = tx.vin[i].prevout; const CCoins* coins = AccessCoins(prevout.hash); @@ -482,6 +486,9 @@ double CCoinsViewCache::GetPriority(const CTransaction &tx, int nHeight) const if (tx.vjoinsplit.size() > 0) { return MAX_PRIORITY; } + if (tx.IsCoinImport()) { + return MAX_PRIORITY; + } double dResult = 0.0; BOOST_FOREACH(const CTxIn& txin, tx.vin) diff --git a/src/coins.h b/src/coins.h index fcc32caae..9cee20712 100644 --- a/src/coins.h +++ b/src/coins.h @@ -97,6 +97,11 @@ public: nVersion = tx.nVersion; //nLockTime = tx.nLockTime; ClearUnspendable(); + + // This must live forever + if (tx.IsCoinImport()) { + vout.insert(vout.begin(), CTxOut(0, CScript() << OP_NOP << OP_RETURN)); + } } //! construct a CCoins from a CTransaction, at a given height diff --git a/src/main.cpp b/src/main.cpp index ab27a998f..104540419 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,6 +10,7 @@ #include "addrman.h" #include "alert.h" #include "arith_uint256.h" +#include "cc/importcoin.h" #include "chainparams.h" #include "checkpoints.h" #include "checkqueue.h" @@ -21,6 +22,7 @@ #include "metrics.h" #include "net.h" #include "pow.h" +#include "script/interpreter.h" #include "txdb.h" #include "txmempool.h" #include "ui_interface.h" @@ -817,7 +819,10 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs, { if (tx.IsCoinBase()) return true; // Coinbases don't use vin normally - + + if (tx.IsCoinImport()) + return tx.vin[0].scriptSig.IsCoinImport(); + for (unsigned int i = 0; i < tx.vin.size(); i++) { const CTxOut& prev = mapInputs.GetOutputFor(tx.vin[i]); @@ -888,7 +893,7 @@ unsigned int GetLegacySigOpCount(const CTransaction& tx) unsigned int GetP2SHSigOpCount(const CTransaction& tx, const CCoinsViewCache& inputs) { - if (tx.IsCoinBase()) + if (tx.IsCoinBase() || tx.IsCoinImport()) return 0; unsigned int nSigOps = 0; @@ -945,8 +950,8 @@ bool ContextualCheckTransaction(const CTransaction& tx, CValidationState &state, return state.DoS(dosLevel, error("ContextualCheckTransaction(): transaction is expired"), REJECT_INVALID, "tx-overwinter-expired"); } } - - if (!(tx.IsCoinBase() || tx.vjoinsplit.empty())) { + + if (!(tx.IsMint() || tx.vjoinsplit.empty())) { auto consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus()); // Empty output script. CScript scriptCode; @@ -1001,7 +1006,7 @@ bool CheckTransaction(const CTransaction& tx, CValidationState &state, if (!CheckTransactionWithoutProofVerification(tx, state)) { return false; } else { - // Ensure that zk-SNARKs verify + // Ensure that zk-SNARKs v|| y BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { if (!joinsplit.Verify(*pzcashParams, verifier, tx.joinSplitPubKey)) { return state.DoS(100, error("CheckTransaction(): joinsplit does not verify"), @@ -1059,6 +1064,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio // Transactions can contain empty `vin` and `vout` so long as // `vjoinsplit` is non-empty. + // Migrations may also have empty `vin` if (tx.vin.empty() && tx.vjoinsplit.empty()) return state.DoS(10, error("CheckTransaction(): vin empty"), REJECT_INVALID, "bad-txns-vin-empty"); @@ -1167,7 +1173,7 @@ bool CheckTransactionWithoutProofVerification(const CTransaction& tx, CValidatio } } - if (tx.IsCoinBase()) + if (tx.IsMint()) { // There should be no joinsplits in a coinbase transaction if (tx.vjoinsplit.size() > 0) @@ -1284,7 +1290,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa if (pool.exists(hash)) { fprintf(stderr,"already in mempool\n"); - return false; + return state.Invalid(false, REJECT_DUPLICATE, "already in mempool"); } // Check for conflicts with in-memory transactions @@ -1326,31 +1332,36 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa view.SetBackend(viewMemPool); // do we already have it? + // This is what stops coin import transactions from being double spent; they + // grow the UTXO set size slightly if (view.HaveCoins(hash)) { fprintf(stderr,"view.HaveCoins(hash) error\n"); - return false; + return state.Invalid(false, REJECT_DUPLICATE, "already have coins"); } - // do all inputs exist? - // Note that this does not check for the presence of actual outputs (see the next check for that), - // and only helps with filling in pfMissingInputs (to determine missing vs spent). - BOOST_FOREACH(const CTxIn txin, tx.vin) + if (!tx.IsCoinImport()) { - if (!view.HaveCoins(txin.prevout.hash)) + // do all inputs exist? + // Note that this does not check for the presence of actual outputs (see the next check for that), + // and only helps with filling in pfMissingInputs (to determine missing vs spent). + BOOST_FOREACH(const CTxIn txin, tx.vin) { - if (pfMissingInputs) - *pfMissingInputs = true; - //fprintf(stderr,"missing inputs\n"); - return false; + if (!view.HaveCoins(txin.prevout.hash)) + { + if (pfMissingInputs) + *pfMissingInputs = true; + //fprintf(stderr,"missing inputs\n"); + return false; + } + } + + // are the actual inputs available? + if (!view.HaveInputs(tx)) + { + //fprintf(stderr,"accept failure.1\n"); + return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); } - } - - // are the actual inputs available? - if (!view.HaveInputs(tx)) - { - //fprintf(stderr,"accept failure.1\n"); - return state.Invalid(error("AcceptToMemoryPool: inputs already spent"),REJECT_DUPLICATE, "bad-txns-inputs-spent"); } // are the joinsplit's requirements met? if (!view.HaveJoinSplitRequirements(tx)) @@ -1393,11 +1404,13 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // Keep track of transactions that spend a coinbase, which we re-scan // during reorgs to ensure COINBASE_MATURITY is still met. bool fSpendsCoinbase = false; - BOOST_FOREACH(const CTxIn &txin, tx.vin) { - const CCoins *coins = view.AccessCoins(txin.prevout.hash); - if (coins->IsCoinBase()) { - fSpendsCoinbase = true; - break; + if (!tx.IsCoinImport()) { + BOOST_FOREACH(const CTxIn &txin, tx.vin) { + const CCoins *coins = view.AccessCoins(txin.prevout.hash); + if (coins->IsCoinBase()) { + fSpendsCoinbase = true; + break; + } } } @@ -1478,6 +1491,7 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa // There is a similar check in CreateNewBlock() to prevent creating // invalid blocks, however allowing such transactions into the mempool // can be exploited as a DoS attack. + // XXX: is this neccesary for CryptoConditions? if (!ContextualCheckInputs(tx, state, view, true, MANDATORY_SCRIPT_VERIFY_FLAGS, true, txdata, Params().GetConsensus(), consensusBranchId)) { fprintf(stderr,"accept failure.10\n"); @@ -1967,7 +1981,7 @@ void static InvalidBlockFound(CBlockIndex *pindex, const CValidationState &state void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txundo, int nHeight) { - if (!tx.IsCoinBase()) // mark inputs spent + if (!tx.IsMint()) // mark inputs spent { txundo.vprevout.reserve(tx.vin.size()); BOOST_FOREACH(const CTxIn &txin, tx.vin) { @@ -2003,7 +2017,8 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) bool CScriptCheck::operator()() { const CScript &scriptSig = ptxTo->vin[nIn].scriptSig; - if (!VerifyScript(scriptSig, scriptPubKey, nFlags, ServerTransactionSignatureChecker(ptxTo, nIn, amount, cacheStore, *txdata), consensusBranchId, &error)) { + ServerTransactionSignatureChecker checker(ptxTo, nIn, amount, cacheStore, *txdata); + if (!VerifyScript(scriptSig, scriptPubKey, nFlags, checker, consensusBranchId, &error)) { return ::error("CScriptCheck(): %s:%d VerifySignature failed: %s", ptxTo->GetHash().ToString(), nIn, ScriptErrorString(error)); } return true; @@ -2113,7 +2128,7 @@ bool ContextualCheckInputs( uint32_t consensusBranchId, std::vector *pvChecks) { - if (!tx.IsCoinBase()) + if (!tx.IsMint()) { if (!Consensus::CheckTxInputs(tx, state, inputs, GetSpendHeight(inputs), consensusParams)) { return false; @@ -2165,7 +2180,13 @@ bool ContextualCheckInputs( } } } - + + if (tx.IsCoinImport()) + { + ServerTransactionSignatureChecker checker(&tx, 0, 0, false, txdata); + return VerifyCoinImport(tx.vin[0].scriptSig, checker, state); + } + return true; } @@ -2400,7 +2421,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } // restore inputs - if (i > 0) { // not coinbases + if (!tx.IsMint()) { const CTxUndo &txundo = blockUndo.vtxundo[i-1]; if (txundo.vprevout.size() != tx.vin.size()) return error("DisconnectBlock(): transaction and undo data inconsistent"); @@ -2681,7 +2702,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin return state.DoS(100, error("ConnectBlock(): too many sigops"), REJECT_INVALID, "bad-blk-sigops"); //fprintf(stderr,"ht.%d vout0 t%u\n",pindex->nHeight,tx.nLockTime); - if (!tx.IsCoinBase()) + if (!tx.IsMint()) { if (!view.HaveInputs(tx)) return state.DoS(100, error("ConnectBlock(): inputs missing/spent"), diff --git a/src/miner.cpp b/src/miner.cpp index 59d1b8a11..f57b53d98 100644 --- a/src/miner.cpp +++ b/src/miner.cpp @@ -11,6 +11,7 @@ #include "amount.h" #include "base58.h" #include "chainparams.h" +#include "cc/importcoin.h" #include "consensus/consensus.h" #include "consensus/upgrades.h" #include "consensus/validation.h" @@ -224,48 +225,55 @@ CBlockTemplate* CreateNewBlock(const CScript& scriptPubKeyIn) double dPriority = 0; CAmount nTotalIn = 0; bool fMissingInputs = false; - BOOST_FOREACH(const CTxIn& txin, tx.vin) + if (tx.IsCoinImport()) { - // Read prev transaction - if (!view.HaveCoins(txin.prevout.hash)) - { - // This should never happen; all transactions in the memory - // pool should connect to either transactions in the chain - // or other transactions in the memory pool. - if (!mempool.mapTx.count(txin.prevout.hash)) - { - LogPrintf("ERROR: mempool transaction missing input\n"); - if (fDebug) assert("mempool transaction missing input" == 0); - fMissingInputs = true; - if (porphan) - vOrphan.pop_back(); - break; - } - - // Has to wait for dependencies - if (!porphan) - { - // Use list for automatic deletion - vOrphan.push_back(COrphan(&tx)); - porphan = &vOrphan.back(); - } - mapDependers[txin.prevout.hash].push_back(porphan); - porphan->setDependsOn.insert(txin.prevout.hash); - nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue; - continue; - } - const CCoins* coins = view.AccessCoins(txin.prevout.hash); - assert(coins); - - CAmount nValueIn = coins->vout[txin.prevout.n].nValue; + CAmount nValueIn = GetCoinImportValue(tx); nTotalIn += nValueIn; - - int nConf = nHeight - coins->nHeight; - - dPriority += (double)nValueIn * nConf; + dPriority += (double)nValueIn * 1000; // flat multiplier + } else { + BOOST_FOREACH(const CTxIn& txin, tx.vin) + { + // Read prev transaction + if (!view.HaveCoins(txin.prevout.hash)) + { + // This should never happen; all transactions in the memory + // pool should connect to either transactions in the chain + // or other transactions in the memory pool. + if (!mempool.mapTx.count(txin.prevout.hash)) + { + LogPrintf("ERROR: mempool transaction missing input\n"); + if (fDebug) assert("mempool transaction missing input" == 0); + fMissingInputs = true; + if (porphan) + vOrphan.pop_back(); + break; + } + + // Has to wait for dependencies + if (!porphan) + { + // Use list for automatic deletion + vOrphan.push_back(COrphan(&tx)); + porphan = &vOrphan.back(); + } + mapDependers[txin.prevout.hash].push_back(porphan); + porphan->setDependsOn.insert(txin.prevout.hash); + nTotalIn += mempool.mapTx.find(txin.prevout.hash)->GetTx().vout[txin.prevout.n].nValue; + continue; + } + const CCoins* coins = view.AccessCoins(txin.prevout.hash); + assert(coins); + + CAmount nValueIn = coins->vout[txin.prevout.n].nValue; + nTotalIn += nValueIn; + + int nConf = nHeight - coins->nHeight; + + dPriority += (double)nValueIn * nConf; + } + nTotalIn += tx.GetJoinSplitValueIn(); } - nTotalIn += tx.GetJoinSplitValueIn(); - + if (fMissingInputs) continue; // Priority is sum(valuein * age) / modified_txsize diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index 35c12abc8..cdaba66c2 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -452,11 +452,21 @@ public: // Compute modified tx size for priority calculation (optionally given tx size) unsigned int CalculateModifiedSize(unsigned int nTxSize=0) const; + bool IsMint() const + { + return (vin.size() == 1 && vin[0].prevout.hash.IsNull()); + } + bool IsCoinBase() const { return (vin.size() == 1 && vin[0].prevout.IsNull()); } + bool IsCoinImport() const + { + return IsMint() && vin[0].prevout.n == 10e8; + } + friend bool operator==(const CTransaction& a, const CTransaction& b) { return a.hash == b.hash; diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index d9cc072e5..d09479008 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -9,7 +9,7 @@ #include "checkpoints.h" #include "base58.h" #include "consensus/validation.h" -#include "cc/betprotocol.h" +#include "cc/eval.h" #include "main.h" #include "primitives/transaction.h" #include "rpcserver.h" @@ -1011,7 +1011,7 @@ UniValue txMoMproof(const UniValue& params, bool fHelp) // Encode and return CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); - ssProof << MoMProof(nIndex, branch, notarisationHash); + ssProof << std::make_pair(notarisationHash, MerkleBranch(nIndex, branch)); return HexStr(ssProof.begin(), ssProof.end()); } diff --git a/src/script/interpreter.cpp b/src/script/interpreter.cpp index 5438102c3..4a2eac2bc 100644 --- a/src/script/interpreter.cpp +++ b/src/script/interpreter.cpp @@ -3,17 +3,20 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + #include "interpreter.h" #include "consensus/upgrades.h" #include "primitives/transaction.h" +#include "cc/eval.h" #include "crypto/ripemd160.h" #include "crypto/sha1.h" #include "crypto/sha256.h" #include "pubkey.h" #include "script/script.h" #include "uint256.h" -#include "cryptoconditions/include/cryptoconditions.h" + using namespace std; diff --git a/src/script/interpreter.h b/src/script/interpreter.h index 7aa0d5099..b4c537a2d 100644 --- a/src/script/interpreter.h +++ b/src/script/interpreter.h @@ -186,5 +186,4 @@ bool VerifyScript( const BaseSignatureChecker& checker, uint32_t consensusBranchId, ScriptError* serror = NULL); - #endif // BITCOIN_SCRIPT_INTERPRETER_H diff --git a/src/script/script.cpp b/src/script/script.cpp index a130df4aa..160c57016 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -8,8 +8,10 @@ #include "tinyformat.h" #include "utilstrencodings.h" #include "script/cc.h" +#include "cc/eval.h" #include "cryptoconditions/include/cryptoconditions.h" + namespace { inline std::string ValueString(const std::vector& vch) { @@ -266,6 +268,17 @@ bool CScript::MayAcceptCryptoCondition() const return out; } +bool CScript::IsCoinImport() const +{ + const_iterator pc = this->begin(); + vector data; + opcodetype opcode; + if (this->GetOp(pc, opcode, data)) + if (opcode > OP_0 && opcode <= OP_PUSHDATA4) + return data.begin()[0] == EVAL_IMPORTCOIN; + return false; +} + bool CScript::IsPushOnly() const { const_iterator pc = begin(); diff --git a/src/script/script.h b/src/script/script.h index e5328cf06..864a92f1f 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -570,6 +570,7 @@ public: bool IsPayToScriptHash() const; bool IsPayToCryptoCondition() const; + bool IsCoinImport() const; bool MayAcceptCryptoCondition() const; /** Called by IsStandardTx and P2SH/BIP62 VerifyScript (which makes it consensus-critical). */ diff --git a/src/test-komodo/main.cpp b/src/test-komodo/main.cpp index 527d7666e..0ce8740b3 100644 --- a/src/test-komodo/main.cpp +++ b/src/test-komodo/main.cpp @@ -1,7 +1,9 @@ #include "key.h" +#include "base58.h" #include "chainparams.h" #include "gtest/gtest.h" #include "crypto/common.h" +#include "testutils.h" int main(int argc, char **argv) { @@ -9,6 +11,11 @@ int main(int argc, char **argv) { ECC_Start(); SelectParams(CBaseChainParams::REGTEST); + CBitcoinSecret vchSecret; + // this returns false due to network prefix mismatch but works anyway + vchSecret.SetString(notarySecret); + notaryKey = vchSecret.GetKey(); + testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp new file mode 100644 index 000000000..6379aac7f --- /dev/null +++ b/src/test-komodo/test_coinimport.cpp @@ -0,0 +1,206 @@ + +#include +#include + +#include "cc/importcoin.h" +#include "cc/eval.h" +#include "base58.h" +#include "core_io.h" +#include "key.h" +#include "main.h" +#include "script/cc.h" +#include "primitives/transaction.h" +#include "script/interpreter.h" +#include "script/serverchecker.h" +#include "txmempool.h" + +#include "testutils.h" + + +extern Eval* EVAL_TEST; + +namespace TestCoinImport { + + +static uint8_t testNum = 0; + +class TestCoinImport : public ::testing::Test, public Eval { +public: + CMutableTransaction burnTx; + std::vector payouts; + MomoProof proof; + uint256 MoMoM; + CMutableTransaction importTx; + uint32_t chainId = 2; + CAmount amount = 100; + + void SetImportTx() { + CTxOut burnOutput = MakeBurnOutput(amount, chainId, payouts); + burnTx.vout.push_back(burnOutput); + MoMoM = burnTx.GetHash(); // TODO: an actual branch + importTx = CMutableTransaction(MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts)); + } + + uint32_t GetCurrentLedgerID() const { return chainId; } + + bool GetNotarisationData(int notarisationHeight, NotarisationData &data, bool verifyCanonical) const + { + if (MoMoM.IsNull()) return false; + data.MoMoM = MoMoM; + return true; + } + +protected: + static void SetUpTestCase() { setupChain(); } + virtual void SetUp() { + ASSETCHAINS_CC = 1; + EVAL_TEST = this; + + std::vector fakepk; + fakepk.resize(33); + fakepk.begin()[0] = testNum++; + payouts.push_back(CTxOut(amount, CScript() << fakepk << OP_CHECKSIG)); + SetImportTx(); + } + + + void TestRunCCEval(CMutableTransaction mtx) + { + CTransaction importTx(mtx); + PrecomputedTransactionData txdata(importTx); + ServerTransactionSignatureChecker checker(&importTx, 0, 0, false, txdata); + CValidationState verifystate; + if (!VerifyCoinImport(importTx.vin[0].scriptSig, checker, verifystate)) + printf("TestRunCCEval: %s\n", verifystate.GetRejectReason().data()); + } +}; + + +TEST_F(TestCoinImport, testProcessImportThroughPipeline) +{ + CValidationState mainstate; + CTransaction tx(importTx); + + // first should work + acceptTxFail(tx); + + // should fail in mempool + ASSERT_FALSE(acceptTx(tx, mainstate)); + EXPECT_EQ("already in mempool", mainstate.GetRejectReason()); + + // should fail in persisted UTXO set + generateBlock(); + ASSERT_FALSE(acceptTx(tx, mainstate)); + EXPECT_EQ("already have coins", mainstate.GetRejectReason()); + ASSERT_TRUE(pcoinsTip->HaveCoins(tx.GetHash())); + + // Now disconnect the block + CValidationState invalstate; + if (!InvalidateBlock(invalstate, chainActive.Tip())) { + FAIL() << invalstate.GetRejectReason(); + } + ASSERT_FALSE(pcoinsTip->HaveCoins(tx.GetHash())); + + // should be back in mempool + ASSERT_FALSE(acceptTx(tx, mainstate)); + EXPECT_EQ("already in mempool", mainstate.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testNoVouts) +{ + importTx.vout.resize(0); + TestRunCCEval(importTx); + EXPECT_EQ("no-vouts", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testInvalidParams) +{ + std::vector payload = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << 'a'); + importTx.vin[0].scriptSig = CScript() << payload; + TestRunCCEval(importTx); + EXPECT_EQ("invalid-params", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testNonCanonical) +{ + importTx.nLockTime = 10; + TestRunCCEval(importTx); + EXPECT_EQ("non-canonical", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testInvalidBurnOutputs) +{ + burnTx.vout.resize(0); + MoMoM = burnTx.GetHash(); // TODO: an actual branch + CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); + TestRunCCEval(tx); + EXPECT_EQ("invalid-burn-outputs", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testInvalidBurnParams) +{ + burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(chainId)); + MoMoM = burnTx.GetHash(); // TODO: an actual branch + CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); + TestRunCCEval(tx); + EXPECT_EQ("invalid-burn-params", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testWrongChainId) +{ + chainId = 0; + TestRunCCEval(importTx); + EXPECT_EQ("importcoin-wrong-chain", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testInvalidBurnAmount) +{ + burnTx.vout[0].nValue = 0; + MoMoM = burnTx.GetHash(); // TODO: an actual branch + CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); + TestRunCCEval(tx); + EXPECT_EQ("invalid-burn-amount", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testPayoutTooHigh) +{ + importTx.vout[0].nValue = 101; + TestRunCCEval(importTx); + EXPECT_EQ("payout-too-high", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testInvalidPayouts) +{ + importTx.vout[0].nValue = 40; + importTx.vout.push_back(importTx.vout[0]); + TestRunCCEval(importTx); + EXPECT_EQ("wrong-payouts", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testCouldntLoadMomom) +{ + MoMoM.SetNull(); + TestRunCCEval(importTx); + EXPECT_EQ("coudnt-load-momom", state.GetRejectReason()); +} + + +TEST_F(TestCoinImport, testMomomCheckFail) +{ + MoMoM.SetNull(); + MoMoM.begin()[0] = 1; + TestRunCCEval(importTx); + EXPECT_EQ("momom-check-fail", state.GetRejectReason()); +} + +} /* namespace TestCoinImport */ diff --git a/src/test-komodo/test_cryptoconditions.cpp b/src/test-komodo/test_cryptoconditions.cpp index f1037a2ae..4fddf9c55 100644 --- a/src/test-komodo/test_cryptoconditions.cpp +++ b/src/test-komodo/test_cryptoconditions.cpp @@ -12,11 +12,6 @@ #include "testutils.h" -CKey notaryKey; - -std::string pubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47"; -std::string secret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU"; - class CCTest : public ::testing::Test { public: @@ -32,11 +27,6 @@ protected: virtual void SetUp() { // enable CC ASSETCHAINS_CC = 1; - // Notary key - CBitcoinSecret vchSecret; - // this returns false due to network prefix mismatch but works anyway - vchSecret.SetString(secret); - notaryKey = vchSecret.GetKey(); } }; diff --git a/src/test-komodo/test_eval_bet.cpp b/src/test-komodo/test_eval_bet.cpp index 6f41608b9..4dfcf1cab 100644 --- a/src/test-komodo/test_eval_bet.cpp +++ b/src/test-komodo/test_eval_bet.cpp @@ -208,7 +208,7 @@ public: int nIndex = 5; std::vector vBranch; vBranch.resize(3); - return MoMProof(nIndex, vBranch, EvalMock::NotarisationHash()); + return {MerkleBranch(nIndex, vBranch), EvalMock::NotarisationHash()}; } CMutableTransaction ImportPayoutTx() @@ -237,7 +237,7 @@ public: eval.currentHeight = currentHeight; MoMProof proof = GetMoMProof(); - eval.MoM = proof.Exec(DisputeTx(Player2).GetHash()); + eval.MoM = proof.branch.Exec(DisputeTx(Player2).GetHash()); EVAL_TEST = &eval; return eval; @@ -585,7 +585,7 @@ TEST_F(TestBet, testImportPayoutMomFail) EvalMock eval = ebet.SetEvalMock(12); MoMProof proof = ebet.GetMoMProof(); - proof.nIndex ^= 1; + proof.branch.nIndex ^= 1; CMutableTransaction importTx = ebet.bet.MakeImportPayoutTx( ebet.Payouts(Player2), ebet.DisputeTx(Player2), uint256(), proof); diff --git a/src/test-komodo/test_eval_notarisation.cpp b/src/test-komodo/test_eval_notarisation.cpp index 86d5f58c4..f0ce45358 100644 --- a/src/test-komodo/test_eval_notarisation.cpp +++ b/src/test-komodo/test_eval_notarisation.cpp @@ -15,7 +15,6 @@ #include "testutils.h" -extern Eval* EVAL_TEST; extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp); @@ -61,7 +60,7 @@ static auto noop = [&](CMutableTransaction &mtx){}; template -void SetEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify) +void SetupEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify) { eval.nNotaries = komodo_notaries(eval.notaries, 780060, 1522946781); @@ -80,8 +79,6 @@ void SetEval(EvalMock &eval, CMutableTransaction ¬ary, Modifier modify) eval.txs[notary.GetHash()] = CTransaction(notary); eval.blocks[notary.GetHash()].nHeight = 780060; eval.blocks[notary.GetHash()].nTime = 1522946781; - - EVAL_TEST = &eval; } @@ -94,12 +91,12 @@ static bool init = DecodeHexTx(notaryTx, rawNotaryTx); static uint256 proofTxHash = uint256S("37f76551a16093fbb0a92ee635bbd45b3460da8fd00cf7d5a6b20d93e727fe4c"); static auto vMomProof = ParseHex("0303faecbdd4b3da128c2cd2701bb143820a967069375b2ec5b612f39bbfe78a8611978871c193457ab1e21b9520f4139f113b8d75892eb93ee247c18bccfd067efed7eacbfcdc8946cf22de45ad536ec0719034fb9bc825048fe6ab61fee5bd6e9aae0bb279738d46673c53d68eb2a72da6dbff215ee41a4d405a74ff7cd355805b"); // $ fiat/bots txMoMproof $proofTxHash - +/* TEST(TestEvalNotarisation, testGetNotarisation) { EvalMock eval; CMutableTransaction notary(notaryTx); - SetEval(eval, notary, noop); + SetupEval(eval, notary, noop); NotarisationData data; ASSERT_TRUE(eval.GetNotarisationData(notary.GetHash(), data)); @@ -111,7 +108,7 @@ TEST(TestEvalNotarisation, testGetNotarisation) MoMProof proof; E_UNMARSHAL(vMomProof, ss >> proof); - EXPECT_EQ(data.MoM, proof.Exec(proofTxHash)); + EXPECT_EQ(data.MoM, proof.branch.Exec(proofTxHash)); } @@ -119,13 +116,14 @@ TEST(TestEvalNotarisation, testInvalidNotaryPubkey) { EvalMock eval; CMutableTransaction notary(notaryTx); - SetEval(eval, notary, noop); + SetupEval(eval, notary, noop); memset(eval.notaries[10], 0, 33); NotarisationData data; ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data)); } +*/ TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn) @@ -134,7 +132,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationBadOpReturn) CMutableTransaction notary(notaryTx); notary.vout[1].scriptPubKey = CScript() << OP_RETURN << 0; - SetEval(eval, notary, noop); + SetupEval(eval, notary, noop); NotarisationData data; ASSERT_FALSE(eval.GetNotarisationData(notary.GetHash(), data)); @@ -146,7 +144,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxNotEnoughSigs) EvalMock eval; CMutableTransaction notary(notaryTx); - SetEval(eval, notary, [](CMutableTransaction &tx) { + SetupEval(eval, notary, [](CMutableTransaction &tx) { tx.vin.resize(10); }); @@ -160,7 +158,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationTxDoesntExist) EvalMock eval; CMutableTransaction notary(notaryTx); - SetEval(eval, notary, noop); + SetupEval(eval, notary, noop); NotarisationData data; ASSERT_FALSE(eval.GetNotarisationData(uint256(), data)); @@ -172,7 +170,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationDupeNotary) EvalMock eval; CMutableTransaction notary(notaryTx); - SetEval(eval, notary, [](CMutableTransaction &tx) { + SetupEval(eval, notary, [](CMutableTransaction &tx) { tx.vin[1] = tx.vin[3]; }); @@ -186,7 +184,7 @@ TEST(TestEvalNotarisation, testInvalidNotarisationInputNotCheckSig) EvalMock eval; CMutableTransaction notary(notaryTx); - SetEval(eval, notary, [&](CMutableTransaction &tx) { + SetupEval(eval, notary, [&](CMutableTransaction &tx) { int i = 1; CMutableTransaction txIn; txIn.vout.resize(1); diff --git a/src/test-komodo/testutils.cpp b/src/test-komodo/testutils.cpp new file mode 100644 index 000000000..c3baf4fb6 --- /dev/null +++ b/src/test-komodo/testutils.cpp @@ -0,0 +1,129 @@ +#include +#include +#include + +#include "core_io.h" +#include "key.h" +#include "main.h" +#include "miner.h" +#include "random.h" +#include "rpcserver.h" +#include "rpcprotocol.h" +#include "txdb.h" +#include "util.h" +#include "utilstrencodings.h" +#include "utiltime.h" +#include "consensus/validation.h" +#include "primitives/transaction.h" +#include "script/cc.h" +#include "script/interpreter.h" + +#include "testutils.h" + + +std::string notaryPubkey = "0205a8ad0c1dbc515f149af377981aab58b836af008d4d7ab21bd76faf80550b47"; +std::string notarySecret = "UxFWWxsf1d7w7K5TvAWSkeX4H95XQKwdwGv49DXwWUTzPTTjHBbU"; +CKey notaryKey; + + +/* + * We need to have control of clock, + * otherwise block production can fail. + */ +int64_t nMockTime; + +extern uint32_t USE_EXTERNAL_PUBKEY; + +void setupChain() +{ + SelectParams(CBaseChainParams::REGTEST); + + // Settings to get block reward + //NOTARY_PUBKEY = _NOTARY_PUBKEY; + USE_EXTERNAL_PUBKEY = 1; + mapArgs["-mineraddress"] = "bogus"; + COINBASE_MATURITY = 1; + // Global mock time + nMockTime = GetTime(); + + // Init blockchain + ClearDatadirCache(); + auto pathTemp = GetTempPath() / strprintf("test_komodo_%li_%i", GetTime(), GetRand(100000)); + boost::filesystem::create_directories(pathTemp); + mapArgs["-datadir"] = pathTemp.string(); + pblocktree = new CBlockTreeDB(1 << 20, true); + CCoinsViewDB *pcoinsdbview = new CCoinsViewDB(1 << 23, true); + pcoinsTip = new CCoinsViewCache(pcoinsdbview); + InitBlockIndex(); +} + + +void generateBlock(CBlock *block) +{ + UniValue params; + params.setArray(); + params.push_back(1); + uint256 blockId; + + SetMockTime(nMockTime++); // CreateNewBlock can fail if not enough time passes + + try { + UniValue out = generate(params, false); + blockId.SetHex(out[0].getValStr()); + } catch (const UniValue& e) { + FAIL() << "failed to create block: " << e.write().data(); + } + if (block) ASSERT_TRUE(ReadBlockFromDisk(*block, mapBlockIndex[blockId])); +} + + +void acceptTxFail(const CTransaction tx) +{ + CValidationState state; + if (!acceptTx(tx, state)) FAIL() << state.GetRejectReason(); +} + + +bool acceptTx(const CTransaction tx, CValidationState &state) +{ + LOCK(cs_main); + return AcceptToMemoryPool(mempool, state, tx, false, NULL); +} + + +static CMutableTransaction spendTx(const CTransaction &txIn, int nOut=0) +{ + CMutableTransaction mtx; + mtx.vin.resize(1); + mtx.vin[0].prevout.hash = txIn.GetHash(); + mtx.vin[0].prevout.n = nOut; + mtx.vout.resize(1); + mtx.vout[0].nValue = txIn.vout[nOut].nValue - 1000; + return mtx; +} + + +/* + * 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. + */ +void getInputTx(CScript scriptPubKey, CTransaction &txIn) +{ + // Get coinbase + CBlock block; + generateBlock(&block); + CTransaction coinbase = block.vtx[0]; + + // Create tx + auto mtx = spendTx(coinbase); + mtx.vout[0].scriptPubKey = scriptPubKey; + uint256 hash = SignatureHash(coinbase.vout[0].scriptPubKey, mtx, 0, SIGHASH_ALL, 0, 0); + std::vector vchSig; + notaryKey.Sign(hash, vchSig); + vchSig.push_back((unsigned char)SIGHASH_ALL); + mtx.vin[0].scriptSig << vchSig; + + // Accept + acceptTxFail(mtx); + txIn = CTransaction(mtx); +} diff --git a/src/test-komodo/testutils.h b/src/test-komodo/testutils.h index df8e88cd9..92d28b587 100644 --- a/src/test-komodo/testutils.h +++ b/src/test-komodo/testutils.h @@ -1,7 +1,7 @@ #ifndef TESTUTILS_H #define TESTUTILS_H -#include "script/cc.h" +#include "main.h" #define VCH(a,b) std::vector(a, a + b) @@ -12,4 +12,16 @@ static char ccjsonerr[1000] = "\0"; if (!o) FAIL() << "bad json: " << ccjsonerr; +extern std::string notaryPubkey; +extern std::string notarySecret; +extern CKey notaryKey; + + +void setupChain(); +void generateBlock(CBlock *block=NULL); +bool acceptTx(const CTransaction tx, CValidationState &state); +void acceptTxFail(const CTransaction tx); +void getInputTx(CScript scriptPubKey, CTransaction &txIn); + + #endif /* TESTUTILS_H */ diff --git a/src/txmempool.cpp b/src/txmempool.cpp index dd985f500..9527dd854 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -104,8 +104,10 @@ bool CTxMemPool::addUnchecked(const uint256& hash, const CTxMemPoolEntry &entry, LOCK(cs); mapTx.insert(entry); const CTransaction& tx = mapTx.find(hash)->GetTx(); - for (unsigned int i = 0; i < tx.vin.size(); i++) - mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + if (!tx.IsCoinImport()) { + for (unsigned int i = 0; i < tx.vin.size(); i++) + mapNextTx[tx.vin[i].prevout] = CInPoint(&tx, i); + } BOOST_FOREACH(const JSDescription &joinsplit, tx.vjoinsplit) { BOOST_FOREACH(const uint256 &nf, joinsplit.nullifiers) { mapNullifiers[nf] = &tx; From 3c7604133e7c5f9153f4c990d7b5f59b993f1809 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Tue, 17 Apr 2018 08:40:02 -0500 Subject: [PATCH 02/38] tombstone for import --- src/cc/importcoin.cpp | 63 +++++++++++++++++++++-------- src/cc/importcoin.h | 6 +++ src/coins.h | 5 --- src/main.cpp | 21 ++++++++-- src/primitives/transaction.h | 4 +- src/test-komodo/test_coinimport.cpp | 39 ++++++++++++++++-- 6 files changed, 109 insertions(+), 29 deletions(-) diff --git a/src/cc/importcoin.cpp b/src/cc/importcoin.cpp index 4c9331e73..d5f626683 100644 --- a/src/cc/importcoin.cpp +++ b/src/cc/importcoin.cpp @@ -1,4 +1,5 @@ #include "cc/importcoin.h" +#include "coins.h" #include "hash.h" #include "script/cc.h" #include "primitives/transaction.h" @@ -16,9 +17,7 @@ CTransaction MakeImportCoinTransaction(const MomoProof proof, const CTransaction std::vector payload = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx); CMutableTransaction mtx; - mtx.vin.resize(1); - mtx.vin[0].scriptSig << payload; - mtx.vin[0].prevout.n = 10e8; + mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload)); mtx.vout = payouts; return CTransaction(mtx); } @@ -95,25 +94,33 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp } -/* - * Required by main - */ -CAmount GetCoinImportValue(const CTransaction &tx) +static bool UnmarshalImportTx(const CTransaction &importTx, MomoProof &proof, CTransaction &burnTx) { - CScript scriptSig = tx.vin[0].scriptSig; + CScript scriptSig = importTx.vin[0].scriptSig; auto pc = scriptSig.begin(); opcodetype opcode; std::vector evalScript; - if (!scriptSig.GetOp(pc, opcode, evalScript)) - return false; - if (pc != scriptSig.end()) - return false; - EvalCode code; + int code; + bool out = false; + if (scriptSig.GetOp(pc, opcode, evalScript)) + if (pc == scriptSig.end()) + out = E_UNMARSHAL(evalScript, ss >> VARINT(code); ss >> proof; ss >> burnTx); + return code == EVAL_IMPORTCOIN && out; +} + + +/* + * Required by main + * TODO: test + */ +CAmount GetCoinImportValue(const CTransaction &tx) +{ MomoProof proof; CTransaction burnTx; - if (!E_UNMARSHAL(evalScript, ss >> proof; ss >> burnTx)) - return 0; - return burnTx.vout.size() ? burnTx.vout[0].nValue : 0; + if (UnmarshalImportTx(tx, proof, burnTx)) { + return burnTx.vout.size() ? burnTx.vout[0].nValue : 0; + } + return 0; } @@ -145,3 +152,27 @@ bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& che return f() ? true : state.Invalid(false, 0, "invalid-coin-import"); } + + +void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight) +{ + uint256 burnHash = importTx.vin[0].prevout.hash; + CCoinsModifier modifier = inputs.ModifyCoins(burnHash); + modifier->nHeight = nHeight; + modifier->nVersion = 1; + modifier->vout.push_back(CTxOut(0, CScript() << OP_0)); +} + + +void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs) +{ + uint256 burnHash = importTx.vin[0].prevout.hash; + inputs.ModifyCoins(burnHash)->Clear(); +} + + +int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs) +{ + uint256 burnHash = importTx.vin[0].prevout.hash; + return inputs.HaveCoins(burnHash); +} diff --git a/src/cc/importcoin.h b/src/cc/importcoin.h index 8773b09a7..13fefd513 100644 --- a/src/cc/importcoin.h +++ b/src/cc/importcoin.h @@ -2,6 +2,7 @@ #define CC_IMPORTCOIN_H #include "cc/eval.h" +#include "coins.h" #include "primitives/transaction.h" #include "script/interpreter.h" #include @@ -30,4 +31,9 @@ CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); + +void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight); +void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs); +int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs); + #endif /* CC_IMPORTCOIN_H */ diff --git a/src/coins.h b/src/coins.h index 9cee20712..fcc32caae 100644 --- a/src/coins.h +++ b/src/coins.h @@ -97,11 +97,6 @@ public: nVersion = tx.nVersion; //nLockTime = tx.nLockTime; ClearUnspendable(); - - // This must live forever - if (tx.IsCoinImport()) { - vout.insert(vout.begin(), CTxOut(0, CScript() << OP_NOP << OP_RETURN)); - } } //! construct a CCoins from a CTransaction, at a given height diff --git a/src/main.cpp b/src/main.cpp index 104540419..67220e891 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1332,15 +1332,19 @@ bool AcceptToMemoryPool(CTxMemPool& pool, CValidationState &state, const CTransa view.SetBackend(viewMemPool); // do we already have it? - // This is what stops coin import transactions from being double spent; they - // grow the UTXO set size slightly if (view.HaveCoins(hash)) { fprintf(stderr,"view.HaveCoins(hash) error\n"); return state.Invalid(false, REJECT_DUPLICATE, "already have coins"); } - if (!tx.IsCoinImport()) + if (tx.IsCoinImport()) + { + // Inverse of normal case; if input exists, it's been spent + if (ExistsImportTombstone(tx, view)) + return state.Invalid(false, REJECT_DUPLICATE, "import tombstone exists"); + } + else { // do all inputs exist? // Note that this does not check for the presence of actual outputs (see the next check for that), @@ -2007,6 +2011,13 @@ void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, CTxUndo &txund } } inputs.ModifyCoins(tx.GetHash())->FromTx(tx, nHeight); // add outputs + + // Unorthodox state + if (tx.IsCoinImport()) { + // add a tombstone for the burnTx + AddImportTombstone(tx, inputs, nHeight); + } + } void UpdateCoins(const CTransaction& tx, CCoinsViewCache& inputs, int nHeight) @@ -2465,6 +2476,10 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } } + else if (tx.IsCoinImport()) + { + RemoveImportTombstone(tx, view); + } } // set the old best anchor back diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index cdaba66c2..88ee9a312 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -454,7 +454,7 @@ public: bool IsMint() const { - return (vin.size() == 1 && vin[0].prevout.hash.IsNull()); + return IsCoinImport() || IsCoinBase(); } bool IsCoinBase() const @@ -464,7 +464,7 @@ public: bool IsCoinImport() const { - return IsMint() && vin[0].prevout.n == 10e8; + return (vin.size() == 1 && vin[0].prevout.n == 10e8); } friend bool operator==(const CTransaction& a, const CTransaction& b) diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp index 6379aac7f..96543d80b 100644 --- a/src/test-komodo/test_coinimport.cpp +++ b/src/test-komodo/test_coinimport.cpp @@ -35,8 +35,8 @@ public: CAmount amount = 100; void SetImportTx() { - CTxOut burnOutput = MakeBurnOutput(amount, chainId, payouts); - burnTx.vout.push_back(burnOutput); + burnTx.vout.resize(0); + burnTx.vout.push_back(MakeBurnOutput(amount, chainId, payouts)); MoMoM = burnTx.GetHash(); // TODO: an actual branch importTx = CMutableTransaction(MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts)); } @@ -88,7 +88,7 @@ TEST_F(TestCoinImport, testProcessImportThroughPipeline) ASSERT_FALSE(acceptTx(tx, mainstate)); EXPECT_EQ("already in mempool", mainstate.GetRejectReason()); - // should fail in persisted UTXO set + // should be in persisted UTXO set generateBlock(); ASSERT_FALSE(acceptTx(tx, mainstate)); EXPECT_EQ("already have coins", mainstate.GetRejectReason()); @@ -107,6 +107,39 @@ TEST_F(TestCoinImport, testProcessImportThroughPipeline) } +TEST_F(TestCoinImport, testImportTombstone) +{ + CValidationState mainstate; + // By setting an unspendable output, there will be no addition to UTXO + // Nonetheless, we dont want to be able to import twice + payouts[0].scriptPubKey = CScript() << OP_RETURN; + SetImportTx(); + MoMoM = burnTx.GetHash(); // TODO: an actual branch + CTransaction tx(importTx); + + // first should work + acceptTxFail(tx); + + // should be in persisted UTXO set + generateBlock(); + ASSERT_FALSE(acceptTx(tx, mainstate)); + EXPECT_EQ("import tombstone exists", mainstate.GetRejectReason()); + ASSERT_TRUE(pcoinsTip->HaveCoins(burnTx.GetHash())); + + // Now disconnect the block + CValidationState invalstate; + if (!InvalidateBlock(invalstate, chainActive.Tip())) { + FAIL() << invalstate.GetRejectReason(); + } + // Tombstone should be gone from utxo set + ASSERT_FALSE(pcoinsTip->HaveCoins(burnTx.GetHash())); + + // should be back in mempool + ASSERT_FALSE(acceptTx(tx, mainstate)); + EXPECT_EQ("already in mempool", mainstate.GetRejectReason()); +} + + TEST_F(TestCoinImport, testNoVouts) { importTx.vout.resize(0); From 20c3ac51c21777c3f98cd1f3ad9223f943a18874 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Wed, 9 May 2018 16:17:28 -0300 Subject: [PATCH 03/38] wip --- .gitignore | 1 + src/Makefile.am | 5 +- src/Makefile.ktest.include | 3 +- src/cc/betprotocol.cpp | 3 +- src/cc/eval.cpp | 48 ++--- src/cc/eval.h | 105 ++++++++--- src/cc/import.cpp | 70 ++++++++ src/cc/utils.cpp | 0 src/cc/utils.h | 34 ++++ src/coins.cpp | 2 +- src/crosschain.cpp | 217 ++++++++++++++++++++++ src/crosschain.h | 21 +++ src/{cc => }/importcoin.cpp | 68 +------ src/{cc => }/importcoin.h | 6 +- src/init.cpp | 4 + src/main.cpp | 12 +- src/miner.cpp | 2 +- src/notarisationdb.cpp | 46 +++++ src/notarisationdb.h | 26 +++ src/primitives/block.cpp | 30 +++- src/primitives/block.h | 4 + src/rpcblockchain.cpp | 103 +++-------- src/script/cc.h | 3 +- src/test-komodo/main.cpp | 1 + src/test-komodo/test_coinimport.cpp | 4 +- src/test-komodo/test_crosschain.cpp | 267 ++++++++++++++++++++++++++++ src/test-komodo/testutils.cpp | 25 ++- src/test-komodo/testutils.h | 2 + 28 files changed, 884 insertions(+), 228 deletions(-) create mode 100644 src/cc/import.cpp create mode 100644 src/cc/utils.cpp create mode 100644 src/cc/utils.h create mode 100644 src/crosschain.cpp create mode 100644 src/crosschain.h rename src/{cc => }/importcoin.cpp (61%) rename src/{cc => }/importcoin.h (93%) create mode 100644 src/notarisationdb.cpp create mode 100644 src/notarisationdb.h create mode 100644 src/test-komodo/test_crosschain.cpp 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 */ From 06c960d2b7eaa380461762bc0baa7db664221992 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Tue, 15 May 2018 22:27:10 -0300 Subject: [PATCH 04/38] m3 test proven --- src/cc/betprotocol.cpp | 2 +- src/cc/eval.h | 17 +- src/cc/import.cpp | 2 +- src/crosschain.cpp | 55 ++++-- src/crosschain.h | 4 +- src/komodo_bitcoind.h | 1 + src/komodo_notary.h | 19 +- src/notarisationdb.cpp | 11 +- src/rpcblockchain.cpp | 3 +- src/test-komodo/test_crosschain.cpp | 212 +++++++++------------ src/test-komodo/test_eval_notarisation.cpp | 150 +++++++-------- src/test-komodo/testutils.cpp | 4 +- 12 files changed, 249 insertions(+), 231 deletions(-) diff --git a/src/cc/betprotocol.cpp b/src/cc/betprotocol.cpp index 1b4834767..9fe9a567d 100644 --- a/src/cc/betprotocol.cpp +++ b/src/cc/betprotocol.cpp @@ -199,7 +199,7 @@ bool Eval::ImportPayout(const std::vector params, const CTransaction &i // Check disputeTx solves momproof from vout[0] { - NotarisationData data; + NotarisationData data(0); if (!GetNotarisationData(proof.notarisationHash, data)) return Invalid("coudnt-load-mom"); diff --git a/src/cc/eval.h b/src/cc/eval.h index 1bbf68f65..0625a47e8 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -124,11 +124,18 @@ extern char ASSETCHAINS_SYMBOL[65]; */ class NotarisationData { + bool IsBack() + { + if (IsBackNotarisation == 2) { + return ASSETCHAINS_SYMBOL[0] != 0; + } + return (bool) IsBackNotarisation; + } public: - bool IsBackNotarisation = 0; + int IsBackNotarisation = 0; uint256 blockHash; uint32_t height; - uint256 txHash; // Only get this guy in asset chains not in KMD + uint256 txHash; char symbol[64] = "\0"; uint256 MoM; uint32_t MoMDepth; @@ -136,7 +143,7 @@ public: uint256 MoMoM; uint32_t MoMoMDepth; - NotarisationData(bool IsBack=0) : IsBackNotarisation(IsBack) {} + NotarisationData(int IsBack=2) : IsBackNotarisation(IsBack) {} ADD_SERIALIZE_METHODS; @@ -144,14 +151,14 @@ public: inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { READWRITE(blockHash); READWRITE(height); - if (IsBackNotarisation || (!ser_action.ForRead() && !txHash.IsNull())) + if (IsBack()) READWRITE(txHash); SerSymbol(s, ser_action); READWRITE(MoM); READWRITE(MoMDepth); if (s.size() == 0) return; READWRITE(ccId); - if (IsBackNotarisation) { + if (IsBack()) { READWRITE(MoMoM); READWRITE(MoMoMDepth); } diff --git a/src/cc/import.cpp b/src/cc/import.cpp index be4929b94..9d9daddc0 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -56,7 +56,7 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp // Check proof confirms existance of burnTx { - NotarisationData data; + NotarisationData data(1); if (!GetNotarisationData(proof.notarisationHeight, data, true)) return Invalid("coudnt-load-momom"); diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 069a53f38..bb2717515 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -1,6 +1,7 @@ #include "cc/eval.h" #include "main.h" #include "notarisationdb.h" +#include "komodo_structs.h" /* On KMD */ @@ -12,6 +13,8 @@ uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vect * including the block height of the last notarisation until the height before the * previous notarisation. */ + *assetChainHeight = -1; + if (targetCCid <= 1) return uint256(); @@ -33,24 +36,31 @@ uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vect if (strcmp(data.symbol, symbol) == 0) { seenOwnNotarisations++; + printf("seenOwnNotarisations:%i\n", seenOwnNotarisations); if (seenOwnNotarisations == 2) goto end; if (seenOwnNotarisations == 1) *assetChainHeight = data.height; // TODO: Needed? - continue; // Don't include own MoMs } - if (seenOwnNotarisations == 1) + if (seenOwnNotarisations == 1) { moms.push_back(data.MoM); + printf("Pushed a MoM@%i:%s\n", kmdHeight-i, data.MoM.GetHex().data()); + } } } end: + printf("GetProofRoot {\n"); + printf(" CC:%i S:%s H:%i\n", targetCCid, symbol, kmdHeight); + for (int i=0; i GetCrossChainProof(uint256 txid, char* targetSymbol, +MerkleBranch GetCrossChainProof(uint256 txid, char* targetSymbol, uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof) { /* @@ -79,15 +89,18 @@ std::pair GetCrossChainProof(uint256 txid, char* targetSym // Get MoMs for kmd height and symbol std::vector moms; int targetChainStartHeight; + printf("Getting Proof Root\n"); 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 */ -std::pair GetAssetchainProof(uint256 hash) +std::pair GetAssetchainProof(uint256 hash, int &npIdx) { - uint256 notarisationHash, MoM,MoMoM; int32_t notarisedHeight, depth; CBlockIndex* blockIndex; + int nIndex; + CBlockIndex* blockIndex; + struct notarized_checkpoint* np; std::vector branch; - int nIndex,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; { uint256 blockHash; @@ -148,14 +169,12 @@ std::pair GetAssetchainProof(uint256 hash) 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) + if (!(np = komodo_npptr_for_height(blockIndex->nHeight, &npIdx))) throw std::runtime_error("notarisation not found"); // index of block in MoM leaves - nIndex = notarisedHeight - blockIndex->nHeight; + printf("notarised at: %i\n", np->notarized_height); + nIndex = np->notarized_height - blockIndex->nHeight; } // build merkle chain from blocks to MoM @@ -163,8 +182,8 @@ std::pair GetAssetchainProof(uint256 hash) // 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; + for (int i=0; iMoMdepth; i++) { + uint256 mRoot = chainActive[np->notarized_height - i]->hashMerkleRoot; CTransaction fakeTx; // first value in CTransaction memory is it's hash memcpy((void*)&fakeTx, mRoot.begin(), 32); @@ -173,7 +192,7 @@ std::pair GetAssetchainProof(uint256 hash) branch = fakeBlock.GetMerkleBranch(nIndex); // Check branch - if (MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex)) + if (np->MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex)) throw std::runtime_error("Failed merkle block->MoM"); } @@ -208,10 +227,10 @@ std::pair GetAssetchainProof(uint256 hash) } // Check the proof - if (MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex)) + if (np->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)); + return std::make_pair(np->notarized_desttxid, MerkleBranch(nIndex, branch)); } diff --git a/src/crosschain.h b/src/crosschain.h index 25fe50e5f..e490fbd0e 100644 --- a/src/crosschain.h +++ b/src/crosschain.h @@ -5,13 +5,13 @@ /* On assetchain */ -std::pair GetAssetchainProof(uint256 hash); +std::pair GetAssetchainProof(uint256 hash, int &npIdx); /* 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, +MerkleBranch GetCrossChainProof(uint256 txid, char* targetSymbol, uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof); /* On assetchain */ diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 2174a8d80..cf72a0715 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1268,6 +1268,7 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) { uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev; + return 1; // TODO: obviously cant commit this if ( !CheckEquihashSolution(pblock, Params()) ) { fprintf(stderr,"komodo_checkPOW slowflag.%d ht.%d CheckEquihashSolution failed\n",slowflag,height); diff --git a/src/komodo_notary.h b/src/komodo_notary.h index 3c81f5115..a28699d8b 100644 --- a/src/komodo_notary.h +++ b/src/komodo_notary.h @@ -384,18 +384,35 @@ int32_t komodo_chosennotary(int32_t *notaryidp,int32_t height,uint8_t *pubkey33, //struct komodo_state *komodo_stateptr(char *symbol,char *dest); -struct notarized_checkpoint *komodo_npptr(int32_t height) +struct notarized_checkpoint *komodo_npptr_for_height(int32_t height, int *idx) { char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; int32_t i; struct komodo_state *sp; struct notarized_checkpoint *np = 0; if ( (sp= komodo_stateptr(symbol,dest)) != 0 ) { for (i=sp->NUM_NPOINTS-1; i>=0; i--) { + *idx = i; np = &sp->NPOINTS[i]; if ( np->MoMdepth > 0 && height > np->notarized_height-np->MoMdepth && height <= np->notarized_height ) return(np); } } + *idx = -1; + return(0); +} + +struct notarized_checkpoint *komodo_npptr(int32_t height) +{ + int idx; + return komodo_npptr_for_height(height, &idx); +} + +struct notarized_checkpoint *komodo_npptr_at(int idx) +{ + char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp; + if ( (sp= komodo_stateptr(symbol,dest)) != 0 ) + if (idx < sp->NUM_NPOINTS) + return &sp->NPOINTS[idx]; return(0); } diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index 6daa5a132..b16968614 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -9,19 +9,18 @@ NotarisationDB *pnotarisations; -NotarisationDB::NotarisationDB(size_t nCacheSize, bool fMemory, bool fWipe) : CLevelDBWrapper(GetDataDir() / "notarisations", nCacheSize, fMemory, fWipe, false, 64) { -} +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); + NotarisationData data; if (ParseNotarisationOpReturn(tx, data)) vNotarisations.push_back(std::make_pair(tx.GetHash(), data)); else @@ -40,7 +39,9 @@ void WriteBackNotarisations(NotarisationsInBlock notarisations) { BOOST_FOREACH(Notarisation &n, notarisations) { - if (n.second.IsBackNotarisation) + if (n.second.IsBackNotarisation) { pnotarisations->Write(n.second.txHash, n); + printf("WriteBackNotarisations {\n m3:%s\n}\n", n.second.MoMoM.GetHex().data()); + } } } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 75454497d..99584c7bb 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -940,7 +940,8 @@ UniValue txMoMproof(const UniValue& params, bool fHelp) hash = uint256S(params[0].get_str()); - std::vector proofData = E_MARSHAL(ss << GetAssetchainProof(hash)); + int npIdx; + std::vector proofData = E_MARSHAL(ss << GetAssetchainProof(hash, npIdx)); return HexStr(proofData); } diff --git a/src/test-komodo/test_crosschain.cpp b/src/test-komodo/test_crosschain.cpp index f7f7d47ab..ebf52fa13 100644 --- a/src/test-komodo/test_crosschain.cpp +++ b/src/test-komodo/test_crosschain.cpp @@ -13,7 +13,9 @@ #include "core_io.h" #include "crosschain.h" #include "key.h" +#include "komodo_structs.h" #include "main.h" +#include "notarisationdb.h" #include "primitives/block.h" #include "primitives/transaction.h" #include "script/cc.h" @@ -26,6 +28,7 @@ extern uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); +extern struct notarized_checkpoint *komodo_npptr_at(int idx); /* @@ -40,8 +43,8 @@ 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); + NotarisationData data(2); + return ParseNotarisationOpReturn(tx, data); // If it parses it's valid } protected: static void SetUpTestCase() { } @@ -52,6 +55,15 @@ protected: }; +uint256 endianHash(uint256 h) +{ + uint256 out; + for (int i=0; i<32; i++) { + out.begin()[31-i] = h.begin()[i]; + } + return out; +} + TEST_F(TestCrossChain, testCreateAndValidateImportProof) { @@ -66,10 +78,13 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) int childPid = fork(); void *ctx = zmq_ctx_new(); void *socket = zmq_socket(ctx, ZMQ_PAIR); + if (!childPid) + strcpy(ASSETCHAINS_SYMBOL, "PIZZA"); setupChain(); std::vector blocks; - blocks.resize(10); - NotarisationData a2kmd, kmd2a(true); + blocks.resize(1000); + NotarisationData a2kmd(0), kmd2a(1); + int numTestNotarisations = 10; auto SendIPC = [&] (std::vector v) { @@ -101,75 +116,93 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) 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); + NotarisationData n(0), back(1); + strcpy(n.symbol, "PIZZA"); + n.ccId = 2; + int height = 0; /* - * Notarisation 2 + * Send notarisations and write backnotarisations */ - 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); + for (int ni=0; ni> back)); + RecordNotarisation(blocks[height].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)); + uint256 txid = blocks[7].vtx[0].GetHash(); + int npIdx; + std::pair proof = GetAssetchainProof(txid, npIdx); + SendIPC(E_MARSHAL(ss << txid; ss << proof)); + + /* + * Test proof + */ + std::pair ccProof; + E_UNMARSHAL(RecvIPC(), ss >> ccProof); + + // Now we have the branch with the hash of the notarisation on KMD + // What we'd like is the notarised height on PIZZA so we can go forward + // to the next backnotarisation, and then to the next, to get the M3. + uint256 result = ccProof.second.Exec(txid); + printf("result m3: %s\n", result.GetHex().data()); + struct notarized_checkpoint* np = komodo_npptr_at(npIdx+1); + std::pair b; + pnotarisations->Read(np->notarized_desttxid, b); + printf("m3@1: %s\n", b.second.MoMoM.GetHex().data()); + + { + printf("RunTestAssetChain.test {\n txid: %s\n momom: %s\n", txid.GetHex().data(), b.second.MoMoM.GetHex().data()); + printf(" idx: %i\n", ccProof.second.nIndex); + for (int i=0; i> 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)); + for (int ni=0; ni> n); + // Grab a coinbase input to fund notarisation + generateBlock(&blocks[++height]); + n.txHash = RecordNotarisation(blocks[height].vtx[0], n); + { + std::vector moms; + int assetChainHeight; + n.MoMoM = GetProofRoot(n.symbol, 2, height, moms, &assetChainHeight); + } + printf("RunTestKmd {\n kmdnotid:%s\n momom:%s\n}\n", n.txHash.GetHex().data(), n.MoMoM.GetHex().data()); + n.IsBackNotarisation = 1; + SendIPC(E_MARSHAL(ss << n)); + } /* * Extend proof */ - std::pair assetChainProof; + std::pair proof; 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); + assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> proof)); + proof.second = GetCrossChainProof(txid, (char*)"PIZZA", 2, proof.first, proof.second); + SendIPC(E_MARSHAL(ss << proof)); }; const char endpoint[] = "ipc://tmpKomodoTestCrossChainSock"; @@ -177,8 +210,9 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) if (!childPid) { assert(0 == zmq_connect(socket, endpoint)); usleep(20000); - RunTestAssetchain(); - exit(0); + int out = RunTestAssetchain(); + if (!out) printf("Assetchain success\n"); + exit(out); } else { assert(0 == zmq_bind(socket, endpoint)); @@ -190,77 +224,13 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) } - /* - - * - * 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); - } - - */ + */ } diff --git a/src/test-komodo/test_eval_notarisation.cpp b/src/test-komodo/test_eval_notarisation.cpp index f0ce45358..68da7d990 100644 --- a/src/test-komodo/test_eval_notarisation.cpp +++ b/src/test-komodo/test_eval_notarisation.cpp @@ -21,84 +21,84 @@ extern int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t t 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 + class EvalMock : public Eval { - memcpy(pubkeys, notaries, sizeof(notaries)); - return nNotaries; - } + public: + uint32_t nNotaries; + uint8_t notaries[64][33]; + std::map txs; + std::map blocks; - bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const - { - auto r = txs.find(hash); - if (r != txs.end()) { - txOut = r->second; - if (blocks.count(hash) > 0) - hashBlock = hash; - return true; + int32_t GetNotaries(uint8_t pubkeys[64][33], int32_t height, uint32_t timestamp) const + { + memcpy(pubkeys, notaries, sizeof(notaries)); + return nNotaries; + } + + bool GetTxUnconfirmed(const uint256 &hash, CTransaction &txOut, uint256 &hashBlock) const + { + auto r = txs.find(hash); + if (r != txs.end()) { + txOut = r->second; + if (blocks.count(hash) > 0) + 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 SetupEval(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; isecond; - return true; - } -}; - -static auto noop = [&](CMutableTransaction &mtx){}; -template -void SetupEval(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 Date: Fri, 18 May 2018 19:29:33 -0300 Subject: [PATCH 05/38] cross chain rpc methods --- src/Makefile.am | 1 + src/cc/eval.cpp | 8 +- src/cc/eval.h | 6 +- src/cc/import.cpp | 8 +- src/crosschain.cpp | 92 +++++++-------- src/crosschain.h | 11 +- src/importcoin.cpp | 6 +- src/importcoin.h | 15 +-- src/notarisationdb.cpp | 16 ++- src/notarisationdb.h | 4 +- src/rpcblockchain.cpp | 168 ---------------------------- src/rpcclient.cpp | 1 - src/rpccrosschain.cpp | 158 ++++++++++++++++++++++++++ src/rpcserver.cpp | 12 +- src/rpcserver.h | 4 +- src/test-komodo/test_coinimport.cpp | 2 +- src/test-komodo/test_crosschain.cpp | 53 ++------- src/test-komodo/testutils.cpp | 4 - 18 files changed, 264 insertions(+), 305 deletions(-) create mode 100644 src/rpccrosschain.cpp diff --git a/src/Makefile.am b/src/Makefile.am index fa475b4e1..f9b19978b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -279,6 +279,7 @@ libbitcoin_server_a_SOURCES = \ pow.cpp \ rest.cpp \ rpcblockchain.cpp \ + rpccrosschain.cpp \ rpcmining.cpp \ rpcmisc.cpp \ rpcnet.cpp \ diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 6dc72f2a0..8ed29da9d 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -154,7 +154,7 @@ uint32_t Eval::GetCurrentLedgerID() const /* - * Get MoM from a notarisation tx hash + * Get MoM from a notarisation tx hash (on KMD) */ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) const { @@ -166,9 +166,11 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) return true; } -bool Eval::GetNotarisationData(int notarisationHeight, NotarisationData &data, bool verifyCanonical) const +/* + * Get MoMoM corresponding to a notarisation tx hash (on assetchain) + */ +bool Eval::GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const { - return false; } diff --git a/src/cc/eval.h b/src/cc/eval.h index 0625a47e8..2a30bba90 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -72,8 +72,7 @@ public: virtual bool GetBlock(uint256 hash, CBlockIndex& blockIdx) 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 GetNotarisationData(int notarisationHeight, NotarisationData &data, - bool verifyCanonical) const; + virtual bool GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const; virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const; virtual uint32_t GetCurrentLedgerID() const; }; @@ -228,6 +227,9 @@ public: }; +typedef std::pair TxProof; + + uint256 GetMerkleRoot(const std::vector& vLeaves); diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 9d9daddc0..202d75468 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -15,7 +15,7 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp if (importTx.vout.size() == 0) return Invalid("no-vouts"); // params - MomoProof proof; + TxProof proof; CTransaction burnTx; if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx)) return Invalid("invalid-params"); @@ -56,11 +56,11 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp // Check proof confirms existance of burnTx { - NotarisationData data(1); - if (!GetNotarisationData(proof.notarisationHeight, data, true)) + uint256 momom; + if (!GetProofRoot(proof.first, momom)) return Invalid("coudnt-load-momom"); - if (data.MoMoM != proof.branch.Exec(burnTx.GetHash())) + if (momom != proof.second.Exec(burnTx.GetHash())) return Invalid("momom-check-fail"); } diff --git a/src/crosschain.cpp b/src/crosschain.cpp index bb2717515..b68b77b33 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -5,29 +5,33 @@ /* On KMD */ -uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vector &moms, int* assetChainHeight) +uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight, + std::vector &moms, uint256 &destNotarisationTxid) { /* * 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. + * + * kmdHeight notarisations-0 notarisations-1 + * | |********************| + * > scan backwards > */ - *assetChainHeight = -1; if (targetCCid <= 1) return uint256(); - int seenOwnNotarisations = 0; + if (kmdHeight < 0 || kmdHeight > chainActive.Height()) + return uint256(); - // TODO: test height out of range - // TODO: Make sure that boundary for moms is notarisation tx not block + int seenOwnNotarisations = 0; for (int i=0; i<1440; i++) { if (i > kmdHeight) break; NotarisationsInBlock notarisations; uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock; - if (!pnotarisations->Read(blockHash, notarisations)) + if (!GetBlockNotarisations(blockHash, notarisations)) continue; BOOST_FOREACH(Notarisation& nota, notarisations) { NotarisationData& data = nota.second; @@ -36,32 +40,24 @@ uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vect if (strcmp(data.symbol, symbol) == 0) { seenOwnNotarisations++; - printf("seenOwnNotarisations:%i\n", seenOwnNotarisations); if (seenOwnNotarisations == 2) goto end; if (seenOwnNotarisations == 1) - *assetChainHeight = data.height; // TODO: Needed? + destNotarisationTxid = nota.first; } - if (seenOwnNotarisations == 1) { + if (seenOwnNotarisations == 1) moms.push_back(data.MoM); - printf("Pushed a MoM@%i:%s\n", kmdHeight-i, data.MoM.GetHex().data()); - } } } end: - printf("GetProofRoot {\n"); - printf(" CC:%i S:%s H:%i\n", targetCCid, symbol, kmdHeight); - for (int i=0; iGetTxConfirmed(notarisationTxid, sourceNotarisation, blockIdx)) + if (eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx)) kmdHeight = blockIdx.nHeight; - else if (eval->GetTxUnconfirmed(notarisationTxid, sourceNotarisation, hashBlock)) + else if (eval->GetTxUnconfirmed(assetChainProof.first, sourceNotarisation, hashBlock)) kmdHeight = chainActive.Tip()->nHeight; else throw std::runtime_error("Notarisation not found"); @@ -88,16 +84,14 @@ MerkleBranch GetCrossChainProof(uint256 txid, char* targetSymbol, // Get MoMs for kmd height and symbol std::vector moms; - int targetChainStartHeight; - printf("Getting Proof Root\n"); - uint256 MoMoM = GetProofRoot(targetSymbol, targetCCid, kmdHeight, moms, &targetChainStartHeight); + uint256 targetChainNotarisationTxid; + uint256 MoMoM = CalculateProofRoot(targetSymbol, targetCCid, kmdHeight, moms, targetChainNotarisationTxid); if (MoMoM.IsNull()) throw std::runtime_error("No MoMs found"); // Find index of source MoM in MoMoM int nIndex; for (nIndex=0; nIndex newBranch; + std::vector vBranch; { CBlock fakeBlock; for (int i=0; i &out) { /* - * Here we are given a notarisation txid, and a proof. - * We go from the notarisation to get the backnotarisation, and verify the proof - * against the MoMoM it contains. + * Here we are given a txid, and a proof. + * We go from the KMD notarisation txid to the backnotarisation, + * then jump to the next backnotarisation, which contains the corresponding MoMoM. */ + Notarisation bn; + if (!GetBackNotarisation(kmdNotarisationTxid, bn)) + return false; + + int npIdx; + struct notarized_checkpoint* np = komodo_npptr_for_height(bn.second.height, &npIdx); + if (!(np = komodo_npptr_at(npIdx+1))) + return false; + + return GetBackNotarisation(np->notarized_desttxid, out); + throw std::runtime_error("Can't get backnotarisation"); } - -struct notarized_checkpoint* komodo_npptr_for_height(int32_t height, int *idx); +struct notarized_checkpoint* komodo_npptr(int32_t height); 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); @@ -155,7 +158,7 @@ int32_t komodo_MoM(int32_t *notarized_htp,uint256 *MoMp,uint256 *kmdtxidp,int32_ * in: txid * out: pair */ -std::pair GetAssetchainProof(uint256 hash, int &npIdx) +TxProof GetAssetchainProof(uint256 hash) { int nIndex; CBlockIndex* blockIndex; @@ -169,11 +172,10 @@ std::pair GetAssetchainProof(uint256 hash, int &npIdx) throw std::runtime_error("cannot find transaction"); blockIndex = mapBlockIndex[blockHash]; - if (!(np = komodo_npptr_for_height(blockIndex->nHeight, &npIdx))) + if (!(np = komodo_npptr(blockIndex->nHeight))) throw std::runtime_error("notarisation not found"); // index of block in MoM leaves - printf("notarised at: %i\n", np->notarized_height); nIndex = np->notarized_height - blockIndex->nHeight; } diff --git a/src/crosschain.h b/src/crosschain.h index e490fbd0e..64bacb759 100644 --- a/src/crosschain.h +++ b/src/crosschain.h @@ -5,17 +5,18 @@ /* On assetchain */ -std::pair GetAssetchainProof(uint256 hash, int &npIdx); +TxProof GetAssetchainProof(uint256 hash); /* On KMD */ -uint256 GetProofRoot(char* symbol, uint32_t targetCCid, int kmdHeight, std::vector &moms, int* assetChainHeight); +uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight, + std::vector &moms, uint256 &destNotarisationTxid); /* On KMD */ -MerkleBranch GetCrossChainProof(uint256 txid, char* targetSymbol, - uint32_t targetCCid, uint256 notarisationTxid, MerkleBranch assetChainProof); +TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid, + const TxProof assetChainProof); /* On assetchain */ -bool ValidateCrossChainProof(uint256 txid, int notarisationHeight, MerkleBranch proof); +bool GetNextBacknotarisation(uint256 txid, std::pair &bn); #endif /* CROSSCHAIN_H */ diff --git a/src/importcoin.cpp b/src/importcoin.cpp index 3874ed7d6..e0c260916 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -13,7 +13,7 @@ * import. If it doesn't contain this it's invalid. The empty OP_RETURN will hang around * in the UTXO set and the transaction will be detected as a duplicate. */ -CTransaction MakeImportCoinTransaction(const MomoProof proof, const CTransaction burnTx, const std::vector payouts) +CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector payouts) { std::vector payload = E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx); @@ -30,7 +30,7 @@ CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector } -static bool UnmarshalImportTx(const CTransaction &importTx, MomoProof &proof, CTransaction &burnTx) +static bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx) { CScript scriptSig = importTx.vin[0].scriptSig; auto pc = scriptSig.begin(); @@ -51,7 +51,7 @@ static bool UnmarshalImportTx(const CTransaction &importTx, MomoProof &proof, CT */ CAmount GetCoinImportValue(const CTransaction &tx) { - MomoProof proof; + TxProof proof; CTransaction burnTx; if (UnmarshalImportTx(tx, proof, burnTx)) { return burnTx.vout.size() ? burnTx.vout[0].nValue : 0; diff --git a/src/importcoin.h b/src/importcoin.h index 6f712a1e9..f59851c44 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -8,22 +8,9 @@ #include -class MomoProof -{ -public: - MerkleBranch branch; - int notarisationHeight; - ADD_SERIALIZE_METHODS; - template - inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { - READWRITE(branch); - READWRITE(notarisationHeight); - } -}; - CAmount GetCoinImportValue(const CTransaction &tx); -CTransaction MakeImportCoinTransaction(const MomoProof proof, +CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector payouts); CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector payouts); diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index b16968614..7ef5a0cf8 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -32,6 +32,18 @@ NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight) } +bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs) +{ + return pnotarisations->Read(blockHash, nibs); +} + + +bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n) +{ + return pnotarisations->Read(notarisationHash, n); +} + + /* * Write an index of KMD notarisation id -> backnotarisation */ @@ -39,9 +51,7 @@ void WriteBackNotarisations(NotarisationsInBlock notarisations) { BOOST_FOREACH(Notarisation &n, notarisations) { - if (n.second.IsBackNotarisation) { + if (n.second.IsBackNotarisation) pnotarisations->Write(n.second.txHash, n); - printf("WriteBackNotarisations {\n m3:%s\n}\n", n.second.MoMoM.GetHex().data()); - } } } diff --git a/src/notarisationdb.h b/src/notarisationdb.h index bd2c15685..0e3d48c00 100644 --- a/src/notarisationdb.h +++ b/src/notarisationdb.h @@ -15,12 +15,12 @@ public: extern NotarisationDB *pnotarisations; - typedef std::pair Notarisation; typedef std::vector NotarisationsInBlock; NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight); - +bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs); +bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n); void WriteBackNotarisations(NotarisationsInBlock notarisations); #endif /* NOTARISATIONDB_H */ diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 99584c7bb..7f191a2b3 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -761,10 +761,6 @@ char *bitcoin_address(char *coinaddr,uint8_t addrtype,uint8_t *pubkey_or_rmd160, //uint32_t komodo_interest_args(int32_t *txheightp,uint32_t *tiptimep,uint64_t *valuep,uint256 hash,int32_t n); int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width); int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); -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); -struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi); -uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); UniValue kvsearch(const UniValue& params, bool fHelp) { @@ -801,170 +797,6 @@ UniValue kvsearch(const UniValue& params, bool fHelp) return ret; } -UniValue allMoMs(const UniValue& params, bool fHelp) -{ - struct komodo_ccdata_entry *allMoMs; uint256 MoMoM; int32_t num,i,kmdstarti,kmdendi; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); - if ( fHelp || params.size() != 2 ) - throw runtime_error("allMoMs kmdstarti kmdendi\n"); - LOCK(cs_main); - kmdstarti = atoi(params[0].get_str().c_str()); - kmdendi = atoi(params[1].get_str().c_str()); - ret.push_back(Pair("kmdstarti",kmdstarti)); - ret.push_back(Pair("kmdendi",kmdendi)); - if ( (allMoMs= komodo_allMoMs(&num,&MoMoM,kmdstarti,kmdendi)) != 0 ) - { - for (i=0; i= height ) - throw runtime_error("calc_MoM illegal height or MoMdepth\n"); - //fprintf(stderr,"height_MoM height.%d\n",height); - MoM = komodo_calcMoM(height,MoMdepth); - ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); - ret.push_back(Pair("height",height)); - ret.push_back(Pair("MoMdepth",MoMdepth)); - ret.push_back(Pair("MoM",MoM.GetHex())); - return ret; -} - -UniValue height_MoM(const UniValue& params, bool fHelp) -{ - int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; uint256 MoM,MoMoM,kmdtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); - if ( fHelp || params.size() != 1 ) - throw runtime_error("height_MoM height\n"); - LOCK(cs_main); - height = atoi(params[0].get_str().c_str()); - if ( height <= 0 ) - { - if ( chainActive.Tip() == 0 ) - { - ret.push_back(Pair("error",(char *)"no active chain yet")); - return(ret); - } - height = chainActive.Tip()->nHeight; - } - //fprintf(stderr,"height_MoM height.%d\n",height); - depth = komodo_MoM(¬arized_height,&MoM,&kmdtxid,height,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi); - ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); - ret.push_back(Pair("height",height)); - ret.push_back(Pair("timestamp",(uint64_t)timestamp)); - if ( depth > 0 ) - { - ret.push_back(Pair("depth",depth)); - ret.push_back(Pair("notarized_height",notarized_height)); - ret.push_back(Pair("MoM",MoM.GetHex())); - ret.push_back(Pair("kmdtxid",kmdtxid.GetHex())); - if ( ASSETCHAINS_SYMBOL[0] != 0 ) - { - ret.push_back(Pair("MoMoM",MoMoM.GetHex())); - ret.push_back(Pair("MoMoMoffset",MoMoMoffset)); - ret.push_back(Pair("MoMoMdepth",MoMoMdepth)); - ret.push_back(Pair("kmdstarti",kmdstarti)); - ret.push_back(Pair("kmdendi",kmdendi)); - } - } else ret.push_back(Pair("error",(char *)"no MoM for height")); - - return ret; -} - -UniValue txMoMproof(const UniValue& params, bool fHelp) -{ - uint256 hash, notarisationHash, MoM,MoMoM; int32_t notarisedHeight, depth; CBlockIndex* blockIndex; - std::vector branch; - 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"); - - hash = uint256S(params[0].get_str()); - - int npIdx; - std::vector proofData = E_MARSHAL(ss << GetAssetchainProof(hash, npIdx)); - 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/rpcclient.cpp b/src/rpcclient.cpp index 5b3f0c9eb..e421feb4c 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -136,7 +136,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "notaries", 2 }, { "height_MoM", 1 }, { "MoMoMdata", 3 }, - { "allMoMs", 2 }, { "txMoMproof", 1 }, { "minerids", 1 }, { "kvsearch", 1 }, diff --git a/src/rpccrosschain.cpp b/src/rpccrosschain.cpp new file mode 100644 index 000000000..a7bb49d2e --- /dev/null +++ b/src/rpccrosschain.cpp @@ -0,0 +1,158 @@ +#include "amount.h" +#include "chain.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "crosschain.h" +#include "base58.h" +#include "consensus/validation.h" +#include "cc/eval.h" +#include "main.h" +#include "primitives/transaction.h" +#include "rpcserver.h" +#include "sync.h" +#include "util.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" + +#include + +#include + +#include + +using namespace std; + +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); +struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi); +uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); + + +UniValue assetchainproof(const UniValue& params, bool fHelp) +{ + uint256 hash; + + // parse params and get notarisation data for tx + if ( fHelp || params.size() != 1) + throw runtime_error("assetchainproof needs a txid"); + + hash = uint256S(params[0].get_str()); + + auto proof = GetAssetchainProof(hash); + auto proofData = E_MARSHAL(ss << proof); + return HexStr(proofData); +} + + +UniValue crosschainproof(const UniValue& params, bool fHelp) +{ + + +} + + +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 height_MoM(const UniValue& params, bool fHelp) +{ + int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; uint256 MoM,MoMoM,kmdtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); + if ( fHelp || params.size() != 1 ) + throw runtime_error("height_MoM height\n"); + LOCK(cs_main); + height = atoi(params[0].get_str().c_str()); + if ( height <= 0 ) + { + if ( chainActive.Tip() == 0 ) + { + ret.push_back(Pair("error",(char *)"no active chain yet")); + return(ret); + } + height = chainActive.Tip()->nHeight; + } + //fprintf(stderr,"height_MoM height.%d\n",height); + depth = komodo_MoM(¬arized_height,&MoM,&kmdtxid,height,&MoMoM,&MoMoMoffset,&MoMoMdepth,&kmdstarti,&kmdendi); + ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); + ret.push_back(Pair("height",height)); + ret.push_back(Pair("timestamp",(uint64_t)timestamp)); + if ( depth > 0 ) + { + ret.push_back(Pair("depth",depth)); + ret.push_back(Pair("notarized_height",notarized_height)); + ret.push_back(Pair("MoM",MoM.GetHex())); + ret.push_back(Pair("kmdtxid",kmdtxid.GetHex())); + if ( ASSETCHAINS_SYMBOL[0] != 0 ) + { + ret.push_back(Pair("MoMoM",MoMoM.GetHex())); + ret.push_back(Pair("MoMoMoffset",MoMoMoffset)); + ret.push_back(Pair("MoMoMdepth",MoMoMdepth)); + ret.push_back(Pair("kmdstarti",kmdstarti)); + ret.push_back(Pair("kmdendi",kmdendi)); + } + } else ret.push_back(Pair("error",(char *)"no MoM for height")); + + return ret; +} + +UniValue MoMoMdata(const UniValue& params, bool fHelp) +{ + if ( fHelp || params.size() != 3 ) + throw runtime_error("MoMoMdata symbol kmdheight ccid\n"); + UniValue ret(UniValue::VOBJ); + char* symbol = (char *)params[0].get_str().c_str(); + int kmdheight = atoi(params[1].get_str().c_str()); + int ccid = atoi(params[2].get_str().c_str()); + ret.push_back(Pair("coin",symbol)); + ret.push_back(Pair("kmdheight",kmdheight)); + ret.push_back(Pair("ccid", ccid)); + + uint256 destNotarisationTxid; + std::vector moms; + uint256 MoMoM = CalculateProofRoot(symbol, ccid, kmdheight, moms, destNotarisationTxid); + + UniValue valMoms(UniValue::VARR); + for (int i=0; i= height ) + throw runtime_error("calc_MoM illegal height or MoMdepth\n"); + //fprintf(stderr,"height_MoM height.%d\n",height); + MoM = komodo_calcMoM(height,MoMdepth); + ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); + ret.push_back(Pair("height",height)); + ret.push_back(Pair("MoMdepth",MoMdepth)); + ret.push_back(Pair("MoM",MoM.GetHex())); + return ret; +} diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index 4eb2270fb..cd579c83c 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -302,15 +302,17 @@ static const CRPCCommand vRPCCommands[] = { "blockchain", "paxpending", &paxpending, true }, { "blockchain", "paxprices", &paxprices, true }, { "blockchain", "notaries", ¬aries, true }, - { "blockchain", "allMoMs", &allMoMs, true }, - { "blockchain", "MoMoMdata", &MoMoMdata, true }, - { "blockchain", "calc_MoM", &calc_MoM, true }, - { "blockchain", "height_MoM", &height_MoM, true }, - { "blockchain", "txMoMproof", &txMoMproof, true }, { "blockchain", "minerids", &minerids, true }, { "blockchain", "kvsearch", &kvsearch, true }, { "blockchain", "kvupdate", &kvupdate, true }, + /* Cross chain utilities */ + { "crosschain", "MoMoMdata", &MoMoMdata, true }, + { "crosschain", "calc_MoM", &calc_MoM, true }, + { "crosschain", "height_MoM", &height_MoM, true }, + { "crosschain", "assetchainproof", &assetchainproof, true }, + { "crosschain", "crosschainproof", &crosschainproof, true }, + /* Mining */ { "mining", "getblocktemplate", &getblocktemplate, true }, { "mining", "getmininginfo", &getmininginfo, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index ce412cdb9..7d7ef5d7e 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -312,11 +312,11 @@ extern UniValue z_validateaddress(const UniValue& params, bool fHelp); // in rpc extern UniValue z_getpaymentdisclosure(const UniValue& params, bool fHelp); // in rpcdisclosure.cpp extern UniValue z_validatepaymentdisclosure(const UniValue ¶ms, bool fHelp); // in rpcdisclosure.cpp -extern UniValue allMoMs(const UniValue& params, bool fHelp); extern UniValue MoMoMdata(const UniValue& params, bool fHelp); extern UniValue calc_MoM(const UniValue& params, bool fHelp); extern UniValue height_MoM(const UniValue& params, bool fHelp); -extern UniValue txMoMproof(const UniValue& params, bool fHelp); +extern UniValue assetchainproof(const UniValue& params, bool fHelp); +extern UniValue crosschainproof(const UniValue& params, bool fHelp); extern UniValue notaries(const UniValue& params, bool fHelp); extern UniValue minerids(const UniValue& params, bool fHelp); extern UniValue kvsearch(const UniValue& params, bool fHelp); diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp index fe2f3ce96..81ccd71d7 100644 --- a/src/test-komodo/test_coinimport.cpp +++ b/src/test-komodo/test_coinimport.cpp @@ -28,7 +28,7 @@ class TestCoinImport : public ::testing::Test, public Eval { public: CMutableTransaction burnTx; std::vector payouts; - MomoProof proof; + TxProof proof; uint256 MoMoM; CMutableTransaction importTx; uint32_t chainId = 2; diff --git a/src/test-komodo/test_crosschain.cpp b/src/test-komodo/test_crosschain.cpp index ebf52fa13..6ff261247 100644 --- a/src/test-komodo/test_crosschain.cpp +++ b/src/test-komodo/test_crosschain.cpp @@ -28,7 +28,6 @@ extern uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); -extern struct notarized_checkpoint *komodo_npptr_at(int idx); /* @@ -109,8 +108,6 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) 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(); }; @@ -139,34 +136,13 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) * Generate proof */ uint256 txid = blocks[7].vtx[0].GetHash(); - int npIdx; - std::pair proof = GetAssetchainProof(txid, npIdx); + TxProof proof = GetAssetchainProof(txid); SendIPC(E_MARSHAL(ss << txid; ss << proof)); + E_UNMARSHAL(RecvIPC(), ss >> proof); - /* - * Test proof - */ - std::pair ccProof; - E_UNMARSHAL(RecvIPC(), ss >> ccProof); - - // Now we have the branch with the hash of the notarisation on KMD - // What we'd like is the notarised height on PIZZA so we can go forward - // to the next backnotarisation, and then to the next, to get the M3. - uint256 result = ccProof.second.Exec(txid); - printf("result m3: %s\n", result.GetHex().data()); - struct notarized_checkpoint* np = komodo_npptr_at(npIdx+1); - std::pair b; - pnotarisations->Read(np->notarized_desttxid, b); - printf("m3@1: %s\n", b.second.MoMoM.GetHex().data()); - - { - printf("RunTestAssetChain.test {\n txid: %s\n momom: %s\n", txid.GetHex().data(), b.second.MoMoM.GetHex().data()); - printf(" idx: %i\n", ccProof.second.nIndex); - for (int i=0; i bn; + if (!GetNextBacknotarisation(proof.first, bn)) return 1; + return proof.second.Exec(txid) == bn.second.MoMoM ? 0 : 1; }; auto RunTestKmd = [&] () @@ -186,10 +162,9 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) n.txHash = RecordNotarisation(blocks[height].vtx[0], n); { std::vector moms; - int assetChainHeight; - n.MoMoM = GetProofRoot(n.symbol, 2, height, moms, &assetChainHeight); + uint256 destNotarisationTxid; + n.MoMoM = CalculateProofRoot(n.symbol, 2, height, moms, destNotarisationTxid); } - printf("RunTestKmd {\n kmdnotid:%s\n momom:%s\n}\n", n.txHash.GetHex().data(), n.MoMoM.GetHex().data()); n.IsBackNotarisation = 1; SendIPC(E_MARSHAL(ss << n)); } @@ -197,11 +172,11 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) /* * Extend proof */ - std::pair proof; + TxProof proof; uint256 txid; // Extend proof to MoMoM assert(E_UNMARSHAL(RecvIPC(), ss >> txid; ss >> proof)); - proof.second = GetCrossChainProof(txid, (char*)"PIZZA", 2, proof.first, proof.second); + proof = GetCrossChainProof(txid, (char*)"PIZZA", 2, proof); SendIPC(E_MARSHAL(ss << proof)); }; @@ -217,20 +192,12 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) else { assert(0 == zmq_bind(socket, endpoint)); RunTestKmd(); - int returnStatus; + int returnStatus; waitpid(childPid, &returnStatus, 0); unlink("tmpKomodoTestCrossChainSock"); ASSERT_EQ(0, returnStatus); } - - /* - * 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. - */ } diff --git a/src/test-komodo/testutils.cpp b/src/test-komodo/testutils.cpp index a6a2b8def..e8b57b6b9 100644 --- a/src/test-komodo/testutils.cpp +++ b/src/test-komodo/testutils.cpp @@ -72,13 +72,9 @@ void generateBlock(CBlock *block) 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(); From b6ef9c089a9b8e3635e4ae9659149bc5d8d58084 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sat, 19 May 2018 12:56:48 -0300 Subject: [PATCH 06/38] fix broken test case --- src/test-komodo/test_coinimport.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp index 81ccd71d7..c1db6cc78 100644 --- a/src/test-komodo/test_coinimport.cpp +++ b/src/test-komodo/test_coinimport.cpp @@ -43,13 +43,14 @@ public: uint32_t GetCurrentLedgerID() const { return chainId; } - bool GetNotarisationData(int notarisationHeight, NotarisationData &data, bool verifyCanonical) const + bool GetProofRoot(uint256 hash, uint256 &momom) const { if (MoMoM.IsNull()) return false; - data.MoMoM = MoMoM; + momom = MoMoM; return true; } + protected: static void SetUpTestCase() { setupChain(); } virtual void SetUp() { From 0df96a2f06e48a3f8ad27183d3e2173fc7635303 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sun, 20 May 2018 01:27:31 -0300 Subject: [PATCH 07/38] fix all tests --- src/cc/eval.cpp | 19 +++-- src/cc/eval.h | 3 +- src/cc/import.cpp | 27 +++--- src/importcoin.cpp | 53 +++++++----- src/importcoin.h | 9 +- src/rpccrosschain.cpp | 125 ++++++++++++++++++++++++++++ src/test-komodo/test_coinimport.cpp | 33 +++++--- src/test-komodo/testutils.cpp | 3 + 8 files changed, 215 insertions(+), 57 deletions(-) diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 8ed29da9d..edd5c6850 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -147,12 +147,6 @@ bool Eval::CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t t } -uint32_t Eval::GetCurrentLedgerID() const -{ - return -1; // TODO -} - - /* * Get MoM from a notarisation tx hash (on KMD) */ @@ -171,6 +165,19 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) */ bool Eval::GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const { + return false; // TODO +} + + +uint32_t Eval::GetAssetchainsCC() const +{ + return ASSETCHAINS_CC; +} + + +std::string Eval::GetAssetchainsSymbol() const +{ + return std::string(ASSETCHAINS_SYMBOL); } diff --git a/src/cc/eval.h b/src/cc/eval.h index 2a30bba90..aacec3847 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -74,7 +74,8 @@ public: virtual bool GetNotarisationData(uint256 notarisationHash, NotarisationData &data) const; virtual bool GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const; virtual bool CheckNotaryInputs(const CTransaction &tx, uint32_t height, uint32_t timestamp) const; - virtual uint32_t GetCurrentLedgerID() const; + virtual uint32_t GetAssetchainsCC() const; + virtual std::string GetAssetchainsSymbol() const; }; diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 202d75468..506b2a989 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -12,30 +12,31 @@ */ bool Eval::ImportCoin(const std::vector params, const CTransaction &importTx, unsigned int nIn) { - if (importTx.vout.size() == 0) return Invalid("no-vouts"); + if (importTx.vout.size() < 2) + return Invalid("too-few-vouts"); // params TxProof proof; CTransaction burnTx; - if (!E_UNMARSHAL(params, ss >> proof; ss >> burnTx)) + std::vector payouts; + + if (!UnmarshalImportTx(importTx, proof, burnTx, payouts)) 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()) + // It should not be at all malleable + if (MakeImportCoinTransaction(proof, burnTx, payouts).GetHash() != importTx.GetHash()) return Invalid("non-canonical"); // burn params - uint32_t chain; // todo + uint32_t targetCcid; + std::string targetSymbol; 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()) + if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCcid, payoutsHash)) + return Invalid("invalid-burn-tx"); + + if (targetCcid != GetAssetchainsCC() || targetSymbol != GetAssetchainsSymbol()) return Invalid("importcoin-wrong-chain"); // check burn amount @@ -51,7 +52,7 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp } // Check burntx shows correct outputs hash - if (payoutsHash != SerializeHash(importTx.vout)) + if (payoutsHash != SerializeHash(payouts)) return Invalid("wrong-payouts"); // Check proof confirms existance of burnTx diff --git a/src/importcoin.cpp b/src/importcoin.cpp index e0c260916..bbada7e99 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -6,42 +6,48 @@ #include "primitives/transaction.h" -/* - * Generate ImportCoin transaction. - * - * Contains an empty OP_RETURN as first output; this is critical for preventing a double - * import. If it doesn't contain this it's invalid. The empty OP_RETURN will hang around - * in the UTXO set and the transaction will be detected as a duplicate. - */ CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector payouts) { - std::vector payload = - E_MARSHAL(ss << EVAL_IMPORTCOIN; ss << proof; ss << burnTx); + std::vector payload = E_MARSHAL(ss << EVAL_IMPORTCOIN); CMutableTransaction mtx; mtx.vin.push_back(CTxIn(COutPoint(burnTx.GetHash(), 10e8), CScript() << payload)); mtx.vout = payouts; + auto importData = E_MARSHAL(ss << proof; ss << burnTx); + mtx.vout.insert(mtx.vout.begin(), CTxOut(0, CScript() << OP_RETURN << importData)); return CTransaction(mtx); } -CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector payouts) + +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts) { - std::vector opret = E_MARSHAL(ss << VARINT(targetChain); ss << SerializeHash(payouts)); + std::vector opret = E_MARSHAL(ss << VARINT(targetCCid); + ss << targetSymbol; + ss << SerializeHash(payouts)); return CTxOut(value, CScript() << OP_RETURN << opret); } -static bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx) +bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx, + std::vector &payouts) { - CScript scriptSig = importTx.vin[0].scriptSig; - auto pc = scriptSig.begin(); - opcodetype opcode; - std::vector evalScript; - int code; - bool out = false; - if (scriptSig.GetOp(pc, opcode, evalScript)) - if (pc == scriptSig.end()) - out = E_UNMARSHAL(evalScript, ss >> VARINT(code); ss >> proof; ss >> burnTx); - return code == EVAL_IMPORTCOIN && out; + std::vector vData; + GetOpReturnData(importTx.vout[0].scriptPubKey, vData); + if (importTx.vout.size() < 1) return false; + payouts = std::vector(importTx.vout.begin()+1, importTx.vout.end()); + return importTx.vin.size() == 1 && + importTx.vin[0].scriptSig == (CScript() << E_MARSHAL(ss << EVAL_IMPORTCOIN)) && + E_UNMARSHAL(vData, ss >> proof; ss >> burnTx); +} + + +bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash) +{ + std::vector burnOpret; + if (burnTx.vout.size() == 0) return false; + GetOpReturnData(burnTx.vout[0].scriptPubKey, burnOpret); + return E_UNMARSHAL(burnOpret, ss >> VARINT(*targetCCid); + ss >> targetSymbol; + ss >> payoutsHash); } @@ -53,7 +59,8 @@ CAmount GetCoinImportValue(const CTransaction &tx) { TxProof proof; CTransaction burnTx; - if (UnmarshalImportTx(tx, proof, burnTx)) { + std::vector payouts; + if (UnmarshalImportTx(tx, proof, burnTx, payouts)) { return burnTx.vout.size() ? burnTx.vout[0].nValue : 0; } return 0; diff --git a/src/importcoin.h b/src/importcoin.h index f59851c44..8e43f9376 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -13,10 +13,13 @@ CAmount GetCoinImportValue(const CTransaction &tx); CTransaction MakeImportCoinTransaction(const TxProof proof, const CTransaction burnTx, const std::vector payouts); -CTxOut MakeBurnOutput(CAmount value, int targetChain, const std::vector payouts); +CTxOut MakeBurnOutput(CAmount value, uint32_t targetCCid, std::string targetSymbol, const std::vector payouts); -bool VerifyCoinImport(const CScript& scriptSig, - TransactionSignatureChecker& checker, CValidationState &state); +bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint32_t *targetCCid, uint256 &payoutsHash); +bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransaction &burnTx, + std::vector &payouts); + +bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight); diff --git a/src/rpccrosschain.cpp b/src/rpccrosschain.cpp index a7bb49d2e..c66a805e7 100644 --- a/src/rpccrosschain.cpp +++ b/src/rpccrosschain.cpp @@ -3,9 +3,11 @@ #include "chainparams.h" #include "checkpoints.h" #include "crosschain.h" +#include "importcoin.h" #include "base58.h" #include "consensus/validation.h" #include "cc/eval.h" +#include "cc/utils.h" #include "main.h" #include "primitives/transaction.h" #include "rpcserver.h" @@ -156,3 +158,126 @@ UniValue calc_MoM(const UniValue& params, bool fHelp) ret.push_back(Pair("MoM",MoM.GetHex())); return ret; } + + +UniValue migrate_converttoexport(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 3) + throw runtime_error( + "migrate_converttoexport \"hexstring\" \"dest_symbol\" \"burn_amount\"\n" + "\nConvert a raw transaction to a cross-chain export.\n" + "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 vouts, should be passed to " + "the \"importtransaction\" method on a KMD node to get the corresponding " + "import transaction.\n" + ); + + if (ASSETCHAINS_CC < 2) + throw runtime_error("-ac_cc < 2"); + + if (ASSETCHAINS_SYMBOL[0] == 0) + throw runtime_error("Must be called on assetchain"); + + vector txData(ParseHexV(params[0], "argument 1")); + CMutableTransaction tx; + if (!E_UNMARSHAL(txData, ss >> tx)) + throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); + + string targetSymbol = params[1].get_str(); + if (targetSymbol.size() == 0 || targetSymbol.size() > 32) + throw runtime_error("targetSymbol length must be >0 and <=32"); + + CAmount burnAmount = params[2].get_int64(); + { + CAmount needed; + for (int i=0; i txData(ParseHexV(params[0], "argument 1")); + + CTransaction burnTx; + if (!E_UNMARSHAL(txData, ss >> burnTx)) + throw runtime_error("Couldn't parse burnTx"); + + + vector payouts; + if (!E_UNMARSHAL(ParseHexV(params[0], "argument 2"), ss >> payouts)) + throw runtime_error("Couldn't parse payouts"); + + uint256 txid = burnTx.GetHash(); + TxProof proof = GetAssetchainProof(burnTx.GetHash()); + + CTransaction importTx = MakeImportCoinTransaction(proof, burnTx, payouts); + return HexStr(E_MARSHAL(ss << importTx)); +} + + +UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error(""); + + if (ASSETCHAINS_SYMBOL[0] != 0) + throw runtime_error("Must be called on KMD"); + + CTransaction importTx; + if (!E_UNMARSHAL(ParseHexV(params[0], "argument 2"), ss >> importTx)) + throw runtime_error("Couldn't parse importTx"); + + TxProof proof; + CTransaction burnTx; + vector payouts; + if (!UnmarshalImportTx(importTx, proof, burnTx, payouts)) + throw runtime_error("Couldn't parse importTx data"); + + std::string targetSymbol; + uint32_t targetCCid; + uint256 payoutsHash; + if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash)) + throw runtime_error("Couldn't parse burnTx data"); + + proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof); + + importTx = MakeImportCoinTransaction(proof, burnTx, importTx.vout); + + return HexStr(E_MARSHAL(ss << importTx)); +} diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp index c1db6cc78..3cd6eff6e 100644 --- a/src/test-komodo/test_coinimport.cpp +++ b/src/test-komodo/test_coinimport.cpp @@ -31,17 +31,19 @@ public: TxProof proof; uint256 MoMoM; CMutableTransaction importTx; - uint32_t chainId = 2; + uint32_t testCcid = 2; + std::string testSymbol = "PIZZA"; CAmount amount = 100; void SetImportTx() { burnTx.vout.resize(0); - burnTx.vout.push_back(MakeBurnOutput(amount, chainId, payouts)); - MoMoM = burnTx.GetHash(); // TODO: an actual branch + burnTx.vout.push_back(MakeBurnOutput(amount, testCcid, testSymbol, payouts)); importTx = CMutableTransaction(MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts)); + MoMoM = burnTx.GetHash(); // TODO: an actual branch } - uint32_t GetCurrentLedgerID() const { return chainId; } + uint32_t GetAssetchainsCC() const { return testCcid; } + std::string GetAssetchainsSymbol() const { return testSymbol; } bool GetProofRoot(uint256 hash, uint256 &momom) const { @@ -145,7 +147,7 @@ TEST_F(TestCoinImport, testNoVouts) { importTx.vout.resize(0); TestRunCCEval(importTx); - EXPECT_EQ("no-vouts", state.GetRejectReason()); + EXPECT_EQ("too-few-vouts", state.GetRejectReason()); } @@ -172,23 +174,23 @@ TEST_F(TestCoinImport, testInvalidBurnOutputs) MoMoM = burnTx.GetHash(); // TODO: an actual branch CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); TestRunCCEval(tx); - EXPECT_EQ("invalid-burn-outputs", state.GetRejectReason()); + EXPECT_EQ("invalid-burn-tx", state.GetRejectReason()); } TEST_F(TestCoinImport, testInvalidBurnParams) { - burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(chainId)); + burnTx.vout[0].scriptPubKey = CScript() << OP_RETURN << E_MARSHAL(ss << VARINT(testCcid)); MoMoM = burnTx.GetHash(); // TODO: an actual branch CTransaction tx = MakeImportCoinTransaction(proof, CTransaction(burnTx), payouts); TestRunCCEval(tx); - EXPECT_EQ("invalid-burn-params", state.GetRejectReason()); + EXPECT_EQ("invalid-burn-tx", state.GetRejectReason()); } TEST_F(TestCoinImport, testWrongChainId) { - chainId = 0; + testCcid = 0; TestRunCCEval(importTx); EXPECT_EQ("importcoin-wrong-chain", state.GetRejectReason()); } @@ -206,15 +208,24 @@ TEST_F(TestCoinImport, testInvalidBurnAmount) TEST_F(TestCoinImport, testPayoutTooHigh) { - importTx.vout[0].nValue = 101; + importTx.vout[1].nValue = 101; TestRunCCEval(importTx); EXPECT_EQ("payout-too-high", state.GetRejectReason()); } +TEST_F(TestCoinImport, testAmountInOpret) +{ + importTx.vout[0].nValue = 1; + TestRunCCEval(importTx); + EXPECT_EQ("non-canonical", state.GetRejectReason()); +} + + + TEST_F(TestCoinImport, testInvalidPayouts) { - importTx.vout[0].nValue = 40; + importTx.vout[1].nValue = 40; importTx.vout.push_back(importTx.vout[0]); TestRunCCEval(importTx); EXPECT_EQ("wrong-payouts", state.GetRejectReason()); diff --git a/src/test-komodo/testutils.cpp b/src/test-komodo/testutils.cpp index e8b57b6b9..59ea4307a 100644 --- a/src/test-komodo/testutils.cpp +++ b/src/test-komodo/testutils.cpp @@ -47,6 +47,9 @@ void setupChain() COINBASE_MATURITY = 1; // Global mock time nMockTime = GetTime(); + + // Unload + UnloadBlockIndex(); // Init blockchain ClearDatadirCache(); From 0b485d3c6649c238630401715ded3dc0d829281b Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Tue, 22 May 2018 15:16:37 -0300 Subject: [PATCH 08/38] rpc methods for cross chain transactions --- src/crosschain.cpp | 31 +++++++++++++++++++++++++++ src/crosschain.h | 3 +-- src/importcoin.cpp | 1 + src/importcoin.h | 1 - src/rpcclient.cpp | 17 +++++++++++---- src/rpccrosschain.cpp | 33 ++++++++++------------------- src/rpcserver.cpp | 3 +++ src/rpcserver.h | 4 ++++ src/test-komodo/test_crosschain.cpp | 13 +++++++++--- 9 files changed, 74 insertions(+), 32 deletions(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index b68b77b33..dc45e994c 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -1,9 +1,14 @@ #include "cc/eval.h" +#include "importcoin.h" #include "main.h" #include "notarisationdb.h" #include "komodo_structs.h" +/* + * This file is built in the server + */ + /* On KMD */ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight, std::vector &moms, uint256 &destNotarisationTxid) @@ -123,6 +128,30 @@ cont: } +/* + * Takes an importTx that has proof leading to assetchain root + * and extends proof to cross chain root + */ +void CompleteImportTransaction(CTransaction &importTx) +{ + TxProof proof; + CTransaction burnTx; + std::vector payouts; + if (!UnmarshalImportTx(importTx, proof, burnTx, payouts)) + throw std::runtime_error("Couldn't parse importTx"); + + std::string targetSymbol; + uint32_t targetCCid; + uint256 payoutsHash; + if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash)) + throw std::runtime_error("Couldn't parse burnTx"); + + proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof); + + importTx = MakeImportCoinTransaction(proof, burnTx, importTx.vout); +} + + struct notarized_checkpoint *komodo_npptr_at(int idx); struct notarized_checkpoint *komodo_npptr_for_height(int32_t height, int *idx); @@ -236,3 +265,5 @@ TxProof GetAssetchainProof(uint256 hash) CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); return std::make_pair(np->notarized_desttxid, MerkleBranch(nIndex, branch)); } + + diff --git a/src/crosschain.h b/src/crosschain.h index 64bacb759..15452ac63 100644 --- a/src/crosschain.h +++ b/src/crosschain.h @@ -10,10 +10,9 @@ TxProof GetAssetchainProof(uint256 hash); /* On KMD */ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight, std::vector &moms, uint256 &destNotarisationTxid); - -/* On KMD */ TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid, const TxProof assetChainProof); +void CompleteImportTransaction(CTransaction &importTx); /* On assetchain */ bool GetNextBacknotarisation(uint256 txid, std::pair &bn); diff --git a/src/importcoin.cpp b/src/importcoin.cpp index bbada7e99..55717fc69 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -1,3 +1,4 @@ +#include "crosschain.h" #include "importcoin.h" #include "cc/utils.h" #include "coins.h" diff --git a/src/importcoin.h b/src/importcoin.h index 8e43f9376..f62bb45a1 100644 --- a/src/importcoin.h +++ b/src/importcoin.h @@ -21,7 +21,6 @@ bool UnmarshalImportTx(const CTransaction &importTx, TxProof &proof, CTransactio bool VerifyCoinImport(const CScript& scriptSig, TransactionSignatureChecker& checker, CValidationState &state); - void AddImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs, int nHeight); void RemoveImportTombstone(const CTransaction &importTx, CCoinsViewCache &inputs); int ExistsImportTombstone(const CTransaction &importTx, const CCoinsViewCache &inputs); diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index e421feb4c..f93dbbb0f 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -134,16 +134,25 @@ static const CRPCConvertParam vRPCConvertParams[] = { "paxprices", 3 }, { "paxpending", 0 }, { "notaries", 2 }, - { "height_MoM", 1 }, - { "MoMoMdata", 3 }, - { "txMoMproof", 1 }, { "minerids", 1 }, { "kvsearch", 1 }, { "kvupdate", 4 }, { "z_importkey", 2 }, { "z_importviewingkey", 2 }, { "z_getpaymentdisclosure", 1}, - { "z_getpaymentdisclosure", 2} + { "z_getpaymentdisclosure", 2}, + + // crosschain + { "assetchainproof", 1}, + { "crosschainproof", 1}, + { "getproofroot", 2}, + { "height_MoM", 1}, + { "MoMoMdata", 3}, + { "calc_MoM", 2}, + { "migrate_converttoexport", 3}, + { "migrate_createimporttransaction", 2}, + { "migrate_completeimporttransaction", 1} + }; class CRPCConvertTable diff --git a/src/rpccrosschain.cpp b/src/rpccrosschain.cpp index c66a805e7..b2414a651 100644 --- a/src/rpccrosschain.cpp +++ b/src/rpccrosschain.cpp @@ -164,12 +164,12 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( - "migrate_converttoexport \"hexstring\" \"dest_symbol\" \"burn_amount\"\n" + "migrate_converttoexport rawTx dest_symbol burn_amount\n" "\nConvert a raw transaction to a cross-chain export.\n" "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 vouts, should be passed to " - "the \"importtransaction\" method on a KMD node to get the corresponding " + "The finished export transaction, plus the payouts, should be passed to " + "the \"migrate_createimporttransaction\" method on a KMD node to get the corresponding " "import transaction.\n" ); @@ -199,7 +199,7 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp) CTxOut burnOut = MakeBurnOutput(burnAmount, ASSETCHAINS_CC, targetSymbol, tx.vout); UniValue ret(UniValue::VOBJ); - ret.push_back(Pair("vouts", HexStr(E_MARSHAL(ss << tx.vout)))); + ret.push_back(Pair("payouts", HexStr(E_MARSHAL(ss << tx.vout)))); tx.vout.clear(); tx.vout.push_back(burnOut); ret.push_back(Pair("exportTx", HexStr(E_MARSHAL(ss << tx)))); @@ -224,7 +224,8 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp) UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 2) - throw runtime_error(""); + throw runtime_error("migrate_createimporttransaction burnTx payouts\n\n" + "Create an importTx given a burnTx and the corresponding payouts, hex encoded"); if (ASSETCHAINS_CC < 2) throw runtime_error("-ac_cc < 2"); @@ -254,30 +255,18 @@ UniValue migrate_createimporttransaction(const UniValue& params, bool fHelp) UniValue migrate_completeimporttransaction(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 1) - throw runtime_error(""); + throw runtime_error("migrate_completeimporttransaction importTx\n\n" + "Takes a cross chain import tx with proof generated on assetchain " + "and extends proof to target chain proof root"); if (ASSETCHAINS_SYMBOL[0] != 0) throw runtime_error("Must be called on KMD"); CTransaction importTx; - if (!E_UNMARSHAL(ParseHexV(params[0], "argument 2"), ss >> importTx)) + if (!E_UNMARSHAL(ParseHexV(params[0], "argument 1"), ss >> importTx)) throw runtime_error("Couldn't parse importTx"); - TxProof proof; - CTransaction burnTx; - vector payouts; - if (!UnmarshalImportTx(importTx, proof, burnTx, payouts)) - throw runtime_error("Couldn't parse importTx data"); - - std::string targetSymbol; - uint32_t targetCCid; - uint256 payoutsHash; - if (!UnmarshalBurnTx(burnTx, targetSymbol, &targetCCid, payoutsHash)) - throw runtime_error("Couldn't parse burnTx data"); - - proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof); - - importTx = MakeImportCoinTransaction(proof, burnTx, importTx.vout); + CompleteImportTransaction(importTx); return HexStr(E_MARSHAL(ss << importTx)); } diff --git a/src/rpcserver.cpp b/src/rpcserver.cpp index cd579c83c..97b2bab1b 100644 --- a/src/rpcserver.cpp +++ b/src/rpcserver.cpp @@ -312,6 +312,9 @@ static const CRPCCommand vRPCCommands[] = { "crosschain", "height_MoM", &height_MoM, true }, { "crosschain", "assetchainproof", &assetchainproof, true }, { "crosschain", "crosschainproof", &crosschainproof, true }, + { "crosschain", "migrate_converttoexport", &migrate_converttoexport, true }, + { "crosschain", "migrate_createimporttransaction", &migrate_createimporttransaction, true }, + { "crosschain", "migrate_completeimporttransaction", &migrate_completeimporttransaction, true }, /* Mining */ { "mining", "getblocktemplate", &getblocktemplate, true }, diff --git a/src/rpcserver.h b/src/rpcserver.h index 7d7ef5d7e..1f64d9d07 100644 --- a/src/rpcserver.h +++ b/src/rpcserver.h @@ -317,6 +317,10 @@ extern UniValue calc_MoM(const UniValue& params, bool fHelp); extern UniValue height_MoM(const UniValue& params, bool fHelp); extern UniValue assetchainproof(const UniValue& params, bool fHelp); extern UniValue crosschainproof(const UniValue& params, bool fHelp); +extern UniValue migrate_converttoexport(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 notaries(const UniValue& params, bool fHelp); extern UniValue minerids(const UniValue& params, bool fHelp); extern UniValue kvsearch(const UniValue& params, bool fHelp); diff --git a/src/test-komodo/test_crosschain.cpp b/src/test-komodo/test_crosschain.cpp index 6ff261247..356a7ab6c 100644 --- a/src/test-komodo/test_crosschain.cpp +++ b/src/test-komodo/test_crosschain.cpp @@ -133,7 +133,7 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) } /* - * Generate proof + * Test a proof */ uint256 txid = blocks[7].vtx[0].GetHash(); TxProof proof = GetAssetchainProof(txid); @@ -141,8 +141,15 @@ TEST_F(TestCrossChain, testCreateAndValidateImportProof) E_UNMARSHAL(RecvIPC(), ss >> proof); std::pair bn; - if (!GetNextBacknotarisation(proof.first, bn)) return 1; - return proof.second.Exec(txid) == bn.second.MoMoM ? 0 : 1; + if (!GetNextBacknotarisation(proof.first, bn)) { + printf("GetNextBackNotarisation failed\n"); + return 1; + } + if (proof.second.Exec(txid) != bn.second.MoMoM) { + printf("MoMom incorrect\n"); + return 1; + } + return 0; }; auto RunTestKmd = [&] () From a748b1a26de7f7f5543c692f4236fca6a82987aa Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Fri, 25 May 2018 14:47:54 -0300 Subject: [PATCH 09/38] recognize btc backnotarisations and include ccid in MoMoMdata rpc --- src/cc/eval.h | 30 +++++++++++++++++++++--------- src/rpccrosschain.cpp | 25 +++---------------------- 2 files changed, 24 insertions(+), 31 deletions(-) diff --git a/src/cc/eval.h b/src/cc/eval.h index aacec3847..66e0c43f1 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -124,13 +124,6 @@ extern char ASSETCHAINS_SYMBOL[65]; */ class NotarisationData { - bool IsBack() - { - if (IsBackNotarisation == 2) { - return ASSETCHAINS_SYMBOL[0] != 0; - } - return (bool) IsBackNotarisation; - } public: int IsBackNotarisation = 0; uint256 blockHash; @@ -149,16 +142,21 @@ public: template inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { + + bool IsBack = IsBackNotarisation; + if (2 == IsBackNotarisation) IsBack = AutoIsBackNotarisation(s, ser_action); + READWRITE(blockHash); READWRITE(height); - if (IsBack()) + if (IsBack) READWRITE(txHash); SerSymbol(s, ser_action); + if (s.size() == 0) return; READWRITE(MoM); READWRITE(MoMDepth); if (s.size() == 0) return; READWRITE(ccId); - if (IsBack()) { + if (IsBack) { READWRITE(MoMoM); READWRITE(MoMoMDepth); } @@ -178,6 +176,20 @@ public: throw std::ios_base::failure("couldn't parse symbol"); s.read(symbol, nullPos-&s[0]+1); } + + template + bool AutoIsBackNotarisation(Stream& s, CSerActionUnserialize act) + { + if (ASSETCHAINS_SYMBOL[0]) return 1; + if (s.size() >= 72 && strcmp("BTC", &s[68]) == 0) return 1; + return 0; + } + + template + bool AutoIsBackNotarisation(Stream& s, CSerActionSerialize act) + { + return !txHash.IsNull(); + } }; diff --git a/src/rpccrosschain.cpp b/src/rpccrosschain.cpp index b2414a651..3aed3a2bb 100644 --- a/src/rpccrosschain.cpp +++ b/src/rpccrosschain.cpp @@ -50,29 +50,10 @@ UniValue assetchainproof(const UniValue& params, bool fHelp) UniValue crosschainproof(const UniValue& params, bool fHelp) { - } -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 height_MoM(const UniValue& params, bool fHelp) { int32_t height,depth,notarized_height,MoMoMdepth,MoMoMoffset,kmdstarti,kmdendi; uint256 MoM,MoMoM,kmdtxid; uint32_t timestamp = 0; UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR); @@ -120,10 +101,10 @@ UniValue MoMoMdata(const UniValue& params, bool fHelp) UniValue ret(UniValue::VOBJ); char* symbol = (char *)params[0].get_str().c_str(); int kmdheight = atoi(params[1].get_str().c_str()); - int ccid = atoi(params[2].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("ccid", ccid)); + ret.push_back(Pair("ccid", (int) ccid)); uint256 destNotarisationTxid; std::vector moms; @@ -134,7 +115,7 @@ UniValue MoMoMdata(const UniValue& params, bool fHelp) ret.push_back(Pair("MoMs", valMoms)); ret.push_back(Pair("notarization_hash", destNotarisationTxid.GetHex())); ret.push_back(Pair("MoMoM", MoMoM.GetHex())); - auto vmomomdata = E_MARSHAL(ss << MoMoM; ss << ((uint32_t)0)); + auto vmomomdata = E_MARSHAL(ss << ccid; ss << MoMoM; ss << ((uint32_t)0)); ret.push_back(Pair("data", HexStr(vmomomdata))); return ret; } From 9de5ae87cf776f2db52959afbf2abb77f9448ea5 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sat, 26 May 2018 13:18:20 -0300 Subject: [PATCH 10/38] remove workaround for tests --- src/komodo_bitcoind.h | 1 - 1 file changed, 1 deletion(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index cf72a0715..2174a8d80 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1268,7 +1268,6 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) { uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev; - return 1; // TODO: obviously cant commit this if ( !CheckEquihashSolution(pblock, Params()) ) { fprintf(stderr,"komodo_checkPOW slowflag.%d ht.%d CheckEquihashSolution failed\n",slowflag,height); From f937f44e103da50ffe8020dc2c8f22a808307806 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sun, 27 May 2018 11:03:38 -0300 Subject: [PATCH 11/38] detect backnotarisation by KMD symbol --- src/Makefile.ktest.include | 3 ++- src/cc/eval.h | 1 + src/test-komodo/test_parse_notarisation.cpp | 23 +++++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 src/test-komodo/test_parse_notarisation.cpp diff --git a/src/Makefile.ktest.include b/src/Makefile.ktest.include index 93d3460e2..07c64caa0 100644 --- a/src/Makefile.ktest.include +++ b/src/Makefile.ktest.include @@ -10,7 +10,8 @@ komodo_test_SOURCES = \ test-komodo/test_coinimport.cpp \ test-komodo/test_eval_bet.cpp \ test-komodo/test_eval_notarisation.cpp \ - test-komodo/test_crosschain.cpp + test-komodo/test_crosschain.cpp \ + test-komodo/test_parse_notarisation.cpp komodo_test_CPPFLAGS = $(komodod_CPPFLAGS) diff --git a/src/cc/eval.h b/src/cc/eval.h index 66e0c43f1..676fd0a73 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -182,6 +182,7 @@ public: { if (ASSETCHAINS_SYMBOL[0]) return 1; if (s.size() >= 72 && strcmp("BTC", &s[68]) == 0) return 1; + if (s.size() >= 72 && strcmp("KMD", &s[68]) == 0) return 1; return 0; } diff --git a/src/test-komodo/test_parse_notarisation.cpp b/src/test-komodo/test_parse_notarisation.cpp new file mode 100644 index 000000000..285f98813 --- /dev/null +++ b/src/test-komodo/test_parse_notarisation.cpp @@ -0,0 +1,23 @@ +#include + +#include "cc/eval.h" +#include "core_io.h" +#include "key.h" + +#include "testutils.h" + + +namespace TestParseNotarisation { + +class TestParseNotarisation : public ::testing::Test, public Eval {}; + + +TEST(TestParseNotarisation, test_ee2fa) +{ + // ee2fa47820a31a979f9f21cb3fedbc484bf9a8957cb6c9acd0af28ced29bdfe1 + std::vector opret = ParseHex("c349ff90f3bce62c1b7b49d1da0423b1a3d9b733130cce825b95b9e047c729066e020d00743a06fdb95ad5775d032b30bbb3680dac2091a0f800cf54c79fd3461ce9b31d4b4d4400"); + NotarisationData nd; + ASSERT_TRUE(E_UNMARSHAL(opret, ss >> nd)); +} + +} From 3fdb3782919079cfea210b530564b916e680cb2b Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Mon, 28 May 2018 17:53:49 -0300 Subject: [PATCH 12/38] workaround for assetchain mining blocks in regtest mode --- src/cc/eval.h | 12 +++++++----- src/komodo_bitcoind.h | 3 +++ src/test-komodo/test_crosschain.cpp | 2 ++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/cc/eval.h b/src/cc/eval.h index 676fd0a73..e4c6a467d 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -144,7 +144,7 @@ public: inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) { bool IsBack = IsBackNotarisation; - if (2 == IsBackNotarisation) IsBack = AutoIsBackNotarisation(s, ser_action); + if (2 == IsBackNotarisation) IsBack = DetectBackNotarisation(s, ser_action); READWRITE(blockHash); READWRITE(height); @@ -178,16 +178,18 @@ public: } template - bool AutoIsBackNotarisation(Stream& s, CSerActionUnserialize act) + bool DetectBackNotarisation(Stream& s, CSerActionUnserialize act) { if (ASSETCHAINS_SYMBOL[0]) return 1; - if (s.size() >= 72 && strcmp("BTC", &s[68]) == 0) return 1; - if (s.size() >= 72 && strcmp("KMD", &s[68]) == 0) return 1; + if (s.size() >= 72) { + if (strcmp("BTC", &s[68]) == 0) return 1; + if (strcmp("KMD", &s[68]) == 0) return 1; + } return 0; } template - bool AutoIsBackNotarisation(Stream& s, CSerActionSerialize act) + bool DetectBackNotarisation(Stream& s, CSerActionSerialize act) { return !txHash.IsNull(); } diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 2174a8d80..f88faf268 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1265,6 +1265,8 @@ int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_ return(isPoS); } +bool KOMODO_TEST_ASSETCHAIN_SKIP_POW = 0; + int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) { uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev; @@ -1304,6 +1306,7 @@ int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height) } else if ( possible == 0 || ASSETCHAINS_SYMBOL[0] != 0 ) { + if (KOMODO_TEST_ASSETCHAIN_SKIP_POW) return(0); fprintf(stderr,"pow violation and no chance it is notary ht.%d %s\n",height,hash.ToString().c_str()); return(-1); } diff --git a/src/test-komodo/test_crosschain.cpp b/src/test-komodo/test_crosschain.cpp index 356a7ab6c..9d24b4d1d 100644 --- a/src/test-komodo/test_crosschain.cpp +++ b/src/test-komodo/test_crosschain.cpp @@ -28,6 +28,7 @@ extern uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); +extern bool KOMODO_TEST_ASSETCHAIN_SKIP_POW; /* @@ -48,6 +49,7 @@ public: protected: static void SetUpTestCase() { } virtual void SetUp() { + KOMODO_TEST_ASSETCHAIN_SKIP_POW = 1; ASSETCHAINS_CC = 1; EVAL_TEST = this; } From f84a3239504603cbecba8e99b95fcec987a3acb6 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Mon, 28 May 2018 20:48:56 -0300 Subject: [PATCH 13/38] log notarisation parse errors to file --- src/notarisationdb.cpp | 2 +- src/test-komodo/test_parse_notarisation.cpp | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index 7ef5a0cf8..42a3ae41f 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -24,7 +24,7 @@ NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight) 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", + LogPrintf("WARNING: Couldn't parse notarisation for tx: %s at height %i\n", tx.GetHash().GetHex().data(), nHeight); } } diff --git a/src/test-komodo/test_parse_notarisation.cpp b/src/test-komodo/test_parse_notarisation.cpp index 285f98813..a61ca2aa3 100644 --- a/src/test-komodo/test_parse_notarisation.cpp +++ b/src/test-komodo/test_parse_notarisation.cpp @@ -20,4 +20,15 @@ TEST(TestParseNotarisation, test_ee2fa) ASSERT_TRUE(E_UNMARSHAL(opret, ss >> nd)); } +TEST(TestParseNotarisation, test__) +{ + // 576e910a1f704207bcbcf724124ff9adc5237f45cb6919589cd0aa152caec424 + std::vector opret = ParseHex("b3ed7fbbfbc027caeeeec81e65489ec5d9cd47cda675a5cbb75b4a845e67cf0ef6330300b5a6bd8385feb833f3be961c9d8a46fcecd36dcdfa42ad81a20a892433722f0b4b4d44004125a06024eae24c11f36ea110acd707b041d5355b6e1b42de5e2614357999c6aa02000d26ad0300000000404b4c000000000005130300500d000061f22ba7d19fe29ac3baebd839af8b7127d1f90755534400"); + NotarisationData nd; + // We can't parse this one + ASSERT_FALSE(E_UNMARSHAL(opret, ss >> nd)); +} + +// for l in `g 'parse notarisation' ~/.komodo/debug.log | pyline 'l.split()[8]'`; do hoek decodeTx '{"hex":"'`src/komodo-cli getrawtransaction "$l"`'"}' | jq '.outputs[1].script.op_return' | pyline 'import base64; print base64.b64decode(l).encode("hex")'; done + } From 0ffacf04dcb1d5a97bbb649e1359d03528f18a12 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Mon, 28 May 2018 21:02:34 -0300 Subject: [PATCH 14/38] take care of some TODOs --- src/cc/eval.cpp | 6 +++++- src/importcoin.cpp | 1 - src/test-komodo/test_coinimport.cpp | 6 ++++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index edd5c6850..2c966f192 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -9,6 +9,7 @@ #include "main.h" #include "chain.h" #include "core_io.h" +#include "crosschain.h" Eval* EVAL_TEST = 0; @@ -165,7 +166,10 @@ bool Eval::GetNotarisationData(const uint256 notaryHash, NotarisationData &data) */ bool Eval::GetProofRoot(uint256 kmdNotarisationHash, uint256 &momom) const { - return false; // TODO + std::pair out; + if (!GetNextBacknotarisation(kmdNotarisationHash, out)) return false; + momom = out.second.MoMoM; + return true; } diff --git a/src/importcoin.cpp b/src/importcoin.cpp index 55717fc69..8b87cb535 100644 --- a/src/importcoin.cpp +++ b/src/importcoin.cpp @@ -54,7 +54,6 @@ bool UnmarshalBurnTx(const CTransaction &burnTx, std::string &targetSymbol, uint /* * Required by main - * TODO: test */ CAmount GetCoinImportValue(const CTransaction &tx) { diff --git a/src/test-komodo/test_coinimport.cpp b/src/test-komodo/test_coinimport.cpp index 3cd6eff6e..eac21428a 100644 --- a/src/test-komodo/test_coinimport.cpp +++ b/src/test-komodo/test_coinimport.cpp @@ -248,4 +248,10 @@ TEST_F(TestCoinImport, testMomomCheckFail) EXPECT_EQ("momom-check-fail", state.GetRejectReason()); } + +TEST_F(TestCoinImport, testGetCoinImportValue) +{ + ASSERT_EQ(100, GetCoinImportValue(importTx)); +} + } /* namespace TestCoinImport */ From 69c67b5117cad7bf52ca3189727419e67f965f7b Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Mon, 28 May 2018 23:31:42 -0300 Subject: [PATCH 15/38] disconnect notarisations --- src/cc/eval.cpp | 1 - src/main.cpp | 19 +++++++++++++------ src/notarisationdb.cpp | 16 +++++++++++++--- src/notarisationdb.h | 3 ++- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/src/cc/eval.cpp b/src/cc/eval.cpp index 2c966f192..25550d1e2 100644 --- a/src/cc/eval.cpp +++ b/src/cc/eval.cpp @@ -201,7 +201,6 @@ bool ParseNotarisationOpReturn(const CTransaction &tx, NotarisationData &data) /* * Misc */ - std::string EvalToStr(EvalCode c) { FOREACH_EVAL(EVAL_GENERATE_STRING); diff --git a/src/main.cpp b/src/main.cpp index a3bd0f515..2e73f2af0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2482,7 +2482,14 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex RemoveImportTombstone(tx, view); } } - + + // Delete from notarisations cache + NotarisationsInBlock nibs; + if (GetBlockNotarisations(block.GetHash(), nibs)) { + pnotarisations->Erase(block.GetHash()); + EraseBackNotarisations(nibs); + } + // set the old best anchor back view.PopAnchor(blockUndo.old_tree_root); @@ -2502,6 +2509,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex return AbortNode(state, "Failed to write address unspent index"); } } + return fClean; } @@ -2836,13 +2844,12 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin 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? - + if (notarisations.size() > 0) { + pnotarisations->Write(block.GetHash(), notarisations); + WriteBackNotarisations(notarisations); + } view.PushAnchor(tree); if (!fJustCheck) { diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index 42a3ae41f..18b9da685 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -47,11 +47,21 @@ bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n) /* * Write an index of KMD notarisation id -> backnotarisation */ -void WriteBackNotarisations(NotarisationsInBlock notarisations) +void WriteBackNotarisations(const NotarisationsInBlock notarisations) { - BOOST_FOREACH(Notarisation &n, notarisations) + BOOST_FOREACH(const Notarisation &n, notarisations) { - if (n.second.IsBackNotarisation) + if (!n.second.txHash.IsNull()) pnotarisations->Write(n.second.txHash, n); } } + + +void EraseBackNotarisations(const NotarisationsInBlock notarisations) +{ + BOOST_FOREACH(const Notarisation &n, notarisations) + { + if (!n.second.txHash.IsNull()) + pnotarisations->Erase(n.second.txHash); + } +} diff --git a/src/notarisationdb.h b/src/notarisationdb.h index 0e3d48c00..27f979784 100644 --- a/src/notarisationdb.h +++ b/src/notarisationdb.h @@ -21,6 +21,7 @@ typedef std::vector NotarisationsInBlock; NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight); bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs); bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n); -void WriteBackNotarisations(NotarisationsInBlock notarisations); +void WriteBackNotarisations(const NotarisationsInBlock notarisations); +void EraseBackNotarisations(const NotarisationsInBlock notarisations); #endif /* NOTARISATIONDB_H */ From c7bcf05da45c7276b5dfe03ea3b49b13c7bb20fa Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Wed, 30 May 2018 18:12:05 -0300 Subject: [PATCH 16/38] fix MoM hash calculation --- src/crosschain.cpp | 20 ++++++++++-------- src/komodo_ccdata.h | 45 ++++++++-------------------------------- src/primitives/block.cpp | 15 ++++++++++---- src/primitives/block.h | 2 ++ src/rpcclient.cpp | 4 ---- src/rpccrosschain.cpp | 15 +++++++------- 6 files changed, 41 insertions(+), 60 deletions(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index dc45e994c..0e9861cf8 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -182,6 +182,8 @@ struct notarized_checkpoint* komodo_npptr(int32_t height); 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); +uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth); + /* * On assetchain * in: txid @@ -210,20 +212,20 @@ TxProof GetAssetchainProof(uint256 hash) // 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; + std::vector leaves, tree; for (int i=0; iMoMdepth; i++) { uint256 mRoot = chainActive[np->notarized_height - i]->hashMerkleRoot; - CTransaction fakeTx; - // first value in CTransaction memory is it's hash - memcpy((void*)&fakeTx, mRoot.begin(), 32); - fakeBlock.vtx.push_back(fakeTx); + leaves.push_back(mRoot); } - branch = fakeBlock.GetMerkleBranch(nIndex); + bool fMutated; + BuildMerkleTree(&fMutated, leaves, tree); + branch = GetMerkleBranch(nIndex, leaves.size(), tree); // Check branch - if (np->MoM != CBlock::CheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex)) + uint256 komodoGets = komodo_calcMoM(np->notarized_height, np->MoMdepth); + uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex); + printf("Komodo gets:%s, we get:%s\n", komodoGets.GetHex().data(), ourResult.GetHex().data()); + if (np->MoM != ourResult) throw std::runtime_error("Failed merkle block->MoM"); } diff --git a/src/komodo_ccdata.h b/src/komodo_ccdata.h index 058e38cee..637d13557 100644 --- a/src/komodo_ccdata.h +++ b/src/komodo_ccdata.h @@ -19,53 +19,28 @@ struct komodo_ccdata *CC_data; int32_t CC_firstheight; -bits256 iguana_merkle(bits256 *tree,int32_t txn_count) -{ - int32_t i,n=0,prev; uint8_t serialized[sizeof(bits256) * 2]; - if ( txn_count == 1 ) - return(tree[0]); - prev = 0; - while ( txn_count > 1 ) - { - if ( (txn_count & 1) != 0 ) - tree[prev + txn_count] = tree[prev + txn_count-1], txn_count++; - n += txn_count; - for (i=0; i> 1)] = bits256_doublesha256(0,serialized,sizeof(serialized)); - } - prev = n; - txn_count >>= 1; - } - return(tree[n]); -} +uint256 BuildMerkleTree(bool* fMutated, const std::vector leaves, std::vector &vMerkleTree); uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth) { - static uint256 zero; bits256 MoM,*tree; CBlockIndex *pindex; int32_t i; + static uint256 zero; CBlockIndex *pindex; int32_t i; std::vector tree, leaves; + bool fMutated; if ( MoMdepth >= height ) return(zero); - tree = (bits256 *)calloc(MoMdepth * 3,sizeof(*tree)); for (i=0; ihashMerkleRoot,sizeof(bits256)); + leaves.push_back(pindex->hashMerkleRoot); else - { - free(tree); return(zero); - } } - MoM = iguana_merkle(tree,MoMdepth); - free(tree); - return(*(uint256 *)&MoM); + return BuildMerkleTree(&fMutated, leaves, tree); } struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t kmdstarti,int32_t kmdendi) { - struct komodo_ccdata_entry *allMoMs=0; bits256 *tree,tmp; struct komodo_ccdata *ccdata,*tmpptr; int32_t i,num,max; + struct komodo_ccdata_entry *allMoMs=0; struct komodo_ccdata *ccdata,*tmpptr; int32_t i,num,max; + bool fMutated; std::vector tree, leaves; num = max = 0; portable_mutex_lock(&KOMODO_CC_mutex); DL_FOREACH_SAFE(CC_data,ccdata,tmpptr) @@ -90,11 +65,9 @@ struct komodo_ccdata_entry *komodo_allMoMs(int32_t *nump,uint256 *MoMoMp,int32_t portable_mutex_unlock(&KOMODO_CC_mutex); if ( (*nump= num) > 0 ) { - tree = (bits256 *)calloc(sizeof(bits256),num*3); for (i=0; i CBlock::GetMerkleBranch(int nIndex) const +std::vector GetMerkleBranch(int nIndex, int nLeaves, const std::vector &vMerkleTree) { - if (vMerkleTree.empty()) - BuildMerkleTree(); std::vector vMerkleBranch; int j = 0; - for (int nSize = vtx.size(); nSize > 1; nSize = (nSize + 1) / 2) + for (int nSize = nLeaves; nSize > 1; nSize = (nSize + 1) / 2) { int i = std::min(nIndex^1, nSize-1); vMerkleBranch.push_back(vMerkleTree[j+i]); @@ -106,6 +104,15 @@ std::vector CBlock::GetMerkleBranch(int nIndex) const return vMerkleBranch; } + +std::vector CBlock::GetMerkleBranch(int nIndex) const +{ + if (vMerkleTree.empty()) + BuildMerkleTree(); + return ::GetMerkleBranch(nIndex, vtx.size(), vMerkleTree); +} + + uint256 CBlock::CheckMerkleBranch(uint256 hash, const std::vector& vMerkleBranch, int nIndex) { if (nIndex == -1) diff --git a/src/primitives/block.h b/src/primitives/block.h index 2d3c0cfe4..6bc06f426 100644 --- a/src/primitives/block.h +++ b/src/primitives/block.h @@ -142,6 +142,8 @@ public: uint256 BuildMerkleTree(bool* fMutated, const std::vector leaves, std::vector &vMerkleTree); +std::vector GetMerkleBranch(int nIndex, int nLeaves, const std::vector &vMerkleTree); + /** * Custom serializer for CBlockHeader that omits the nonce and solution, for use diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index f93dbbb0f..e84e9085c 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -149,10 +149,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "height_MoM", 1}, { "MoMoMdata", 3}, { "calc_MoM", 2}, - { "migrate_converttoexport", 3}, - { "migrate_createimporttransaction", 2}, - { "migrate_completeimporttransaction", 1} - }; class CRPCConvertTable diff --git a/src/rpccrosschain.cpp b/src/rpccrosschain.cpp index 3aed3a2bb..a4b0cb503 100644 --- a/src/rpccrosschain.cpp +++ b/src/rpccrosschain.cpp @@ -145,7 +145,7 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp) { if (fHelp || params.size() != 3) throw runtime_error( - "migrate_converttoexport rawTx dest_symbol burn_amount\n" + "migrate_converttoexport rawTx dest_symbol export_amount\n" "\nConvert a raw transaction to a cross-chain export.\n" "If neccesary, the transaction should be funded using fundrawtransaction.\n" "Finally, the transaction should be signed using signrawtransaction\n" @@ -169,13 +169,14 @@ UniValue migrate_converttoexport(const UniValue& params, bool fHelp) if (targetSymbol.size() == 0 || targetSymbol.size() > 32) throw runtime_error("targetSymbol length must be >0 and <=32"); - CAmount burnAmount = params[2].get_int64(); + CAmount burnAmount = AmountFromValue(params[2]); + if (burnAmount <= 0) + throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for export"); { - CAmount needed; - for (int i=0; i payouts; - if (!E_UNMARSHAL(ParseHexV(params[0], "argument 2"), ss >> payouts)) + if (!E_UNMARSHAL(ParseHexV(params[1], "argument 2"), ss >> payouts)) throw runtime_error("Couldn't parse payouts"); uint256 txid = burnTx.GetHash(); From 91d929221c941491cfba787b707e6cf64723fe79 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Wed, 30 May 2018 21:25:44 -0300 Subject: [PATCH 17/38] update ccid location in notarisation data --- src/cc/eval.h | 6 +++--- src/crosschain.cpp | 9 +++------ src/rpcclient.cpp | 1 - src/rpccrosschain.cpp | 2 +- 4 files changed, 7 insertions(+), 11 deletions(-) diff --git a/src/cc/eval.h b/src/cc/eval.h index e4c6a467d..43cdadea7 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -131,8 +131,8 @@ public: uint256 txHash; char symbol[64] = "\0"; uint256 MoM; - uint32_t MoMDepth; - uint32_t ccId; + uint16_t MoMDepth; + uint16_t ccId; uint256 MoMoM; uint32_t MoMoMDepth; @@ -154,8 +154,8 @@ public: if (s.size() == 0) return; READWRITE(MoM); READWRITE(MoMDepth); - if (s.size() == 0) return; READWRITE(ccId); + if (s.size() == 0) return; if (IsBack) { READWRITE(MoMoM); READWRITE(MoMoMDepth); diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 0e9861cf8..3eb618bd7 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -180,10 +180,6 @@ bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, std::pairnHeight))) throw std::runtime_error("notarisation not found"); @@ -222,9 +221,7 @@ TxProof GetAssetchainProof(uint256 hash) branch = GetMerkleBranch(nIndex, leaves.size(), tree); // Check branch - uint256 komodoGets = komodo_calcMoM(np->notarized_height, np->MoMdepth); uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex); - printf("Komodo gets:%s, we get:%s\n", komodoGets.GetHex().data(), ourResult.GetHex().data()); if (np->MoM != ourResult) throw std::runtime_error("Failed merkle block->MoM"); } diff --git a/src/rpcclient.cpp b/src/rpcclient.cpp index e84e9085c..9d1d0b336 100644 --- a/src/rpcclient.cpp +++ b/src/rpcclient.cpp @@ -147,7 +147,6 @@ static const CRPCConvertParam vRPCConvertParams[] = { "crosschainproof", 1}, { "getproofroot", 2}, { "height_MoM", 1}, - { "MoMoMdata", 3}, { "calc_MoM", 2}, }; diff --git a/src/rpccrosschain.cpp b/src/rpccrosschain.cpp index a4b0cb503..c99581406 100644 --- a/src/rpccrosschain.cpp +++ b/src/rpccrosschain.cpp @@ -115,7 +115,7 @@ UniValue MoMoMdata(const UniValue& params, bool fHelp) ret.push_back(Pair("MoMs", valMoms)); ret.push_back(Pair("notarization_hash", destNotarisationTxid.GetHex())); ret.push_back(Pair("MoMoM", MoMoM.GetHex())); - auto vmomomdata = E_MARSHAL(ss << ccid; ss << MoMoM; ss << ((uint32_t)0)); + auto vmomomdata = E_MARSHAL(ss << MoMoM; ss << ((uint32_t)0)); ret.push_back(Pair("data", HexStr(vmomomdata))); return ret; } From 379e23fcec151742aa04c8d80d297821957db1da Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Thu, 31 May 2018 15:19:46 -0300 Subject: [PATCH 18/38] allow TXSCL notarisations without notary inputs --- src/notarisationdb.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index 18b9da685..3087a563f 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -19,10 +19,24 @@ NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight) for (unsigned int i = 0; i < block.vtx.size(); i++) { CTransaction tx = block.vtx[i]; - if (eval->CheckNotaryInputs(tx, nHeight, block.nTime)) { + + // Special case for TXSCL. Should prob be removed at some point. + bool isTxscl = 0; + { NotarisationData data; if (ParseNotarisationOpReturn(tx, data)) + if (strlen(data.symbol) >= 5 && strncmp(data.symbol, "TXSCL", 5) == 0) + isTxscl = 1; + } + + if (isTxscl || eval->CheckNotaryInputs(tx, nHeight, block.nTime)) { + NotarisationData data; + if (ParseNotarisationOpReturn(tx, data)) { vNotarisations.push_back(std::make_pair(tx.GetHash(), data)); + //printf("Parsed a notarisation for: %s, txid:%s, ccid:%i, momdepth:%i\n", + // data.symbol, tx.GetHash().GetHex().data(), data.ccId, data.MoMDepth); + //if (!data.MoMoM.IsNull()) printf("MoMoM:%s\n", data.MoMoM.GetHex().data()); + } else LogPrintf("WARNING: Couldn't parse notarisation for tx: %s at height %i\n", tx.GetHash().GetHex().data(), nHeight); From fe727b9d922841d15fba8897e4f1a822cf6fe7a5 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sat, 2 Jun 2018 16:13:21 -0300 Subject: [PATCH 19/38] zero members in notarisationdata --- src/cc/eval.h | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/cc/eval.h b/src/cc/eval.h index 43cdadea7..4bcdd5617 100644 --- a/src/cc/eval.h +++ b/src/cc/eval.h @@ -126,15 +126,15 @@ class NotarisationData { public: int IsBackNotarisation = 0; - uint256 blockHash; - uint32_t height; - uint256 txHash; - char symbol[64] = "\0"; - uint256 MoM; - uint16_t MoMDepth; - uint16_t ccId; - uint256 MoMoM; - uint32_t MoMoMDepth; + uint256 blockHash = uint256(); + uint32_t height = 0; + uint256 txHash = uint256(); + char symbol[64] = "\0"; + uint256 MoM = uint256(); + uint16_t MoMDepth = 0; + uint16_t ccId = 0; + uint256 MoMoM = uint256(); + uint32_t MoMoMDepth = 0; NotarisationData(int IsBack=2) : IsBackNotarisation(IsBack) {} From 7776d00b9a508a1f195ffa7702173a21f165ebe9 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sun, 3 Jun 2018 13:22:00 -0300 Subject: [PATCH 20/38] fix MoMdepth in crosschain.cpp --- src/crosschain.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 3eb618bd7..94959544b 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -212,7 +212,8 @@ TxProof GetAssetchainProof(uint256 hash) // build merkle chain from blocks to MoM { std::vector leaves, tree; - for (int i=0; iMoMdepth; i++) { + uint32_t md = np->MoMdepth & 0xffff; // MoMdepth shares space with ccid + for (int i=0; inotarized_height - i]->hashMerkleRoot; leaves.push_back(mRoot); } From 89cfc4272dd6c3a3a7f1a70e6b144b6aea193ccf Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sun, 10 Jun 2018 15:12:10 +0200 Subject: [PATCH 21/38] fixes for import coin --- src/cc/import.cpp | 3 ++- src/crosschain.cpp | 7 ++++--- src/komodo_ccdata.h | 2 +- src/notarisationdb.cpp | 8 ++++++-- src/rpcrawtransaction.cpp | 3 +++ src/test-komodo/test_parse_notarisation.cpp | 20 ++++++++++++++++++++ 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/cc/import.cpp b/src/cc/import.cpp index 506b2a989..5f0b804e8 100644 --- a/src/cc/import.cpp +++ b/src/cc/import.cpp @@ -57,10 +57,11 @@ bool Eval::ImportCoin(const std::vector params, const CTransaction &imp // Check proof confirms existance of burnTx { - uint256 momom; + uint256 momom, target; if (!GetProofRoot(proof.first, momom)) return Invalid("coudnt-load-momom"); + target = proof.second.Exec(burnTx.GetHash()); if (momom != proof.second.Exec(burnTx.GetHash())) return Invalid("momom-check-fail"); } diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 94959544b..dd352b025 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -148,7 +148,7 @@ void CompleteImportTransaction(CTransaction &importTx) proof = GetCrossChainProof(burnTx.GetHash(), targetSymbol.data(), targetCCid, proof); - importTx = MakeImportCoinTransaction(proof, burnTx, importTx.vout); + importTx = MakeImportCoinTransaction(proof, burnTx, payouts); } @@ -187,7 +187,7 @@ struct notarized_checkpoint* komodo_npptr(int32_t height); */ TxProof GetAssetchainProof(uint256 hash) { - int nIndex; + int nIndex, md; CBlockIndex* blockIndex; struct notarized_checkpoint* np; std::vector branch; @@ -207,12 +207,13 @@ TxProof GetAssetchainProof(uint256 hash) // index of block in MoM leaves nIndex = np->notarized_height - blockIndex->nHeight; + // MoMdepth shares space with ccid + md = np->MoMdepth & 0xffff; } // build merkle chain from blocks to MoM { std::vector leaves, tree; - uint32_t md = np->MoMdepth & 0xffff; // MoMdepth shares space with ccid for (int i=0; inotarized_height - i]->hashMerkleRoot; leaves.push_back(mRoot); diff --git a/src/komodo_ccdata.h b/src/komodo_ccdata.h index 6fd065953..012183fc7 100644 --- a/src/komodo_ccdata.h +++ b/src/komodo_ccdata.h @@ -25,7 +25,7 @@ uint256 komodo_calcMoM(int32_t height,int32_t MoMdepth) { static uint256 zero; CBlockIndex *pindex; int32_t i; std::vector tree, leaves; bool fMutated; - MoMdepth &= 0xffff; + MoMdepth &= 0xffff; // In case it includes the ccid if ( MoMdepth >= height ) return(zero); for (i=0; iWrite(n.second.txHash, n); + batch.Write(n.second.txHash, n); } + pnotarisations->WriteBatch(batch, true); } void EraseBackNotarisations(const NotarisationsInBlock notarisations) { + CLevelDBBatch batch; BOOST_FOREACH(const Notarisation &n, notarisations) { if (!n.second.txHash.IsNull()) - pnotarisations->Erase(n.second.txHash); + batch.Erase(n.second.txHash); } + pnotarisations->WriteBatch(batch, true); } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 5f349096a..91dff8bf9 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -134,6 +134,9 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& UniValue in(UniValue::VOBJ); if (tx.IsCoinBase()) in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); + else if (tx.IsCoinImport()) { + in.push_back(Pair("is_import", "1")); + } else { in.push_back(Pair("txid", txin.prevout.hash.GetHex())); in.push_back(Pair("vout", (int64_t)txin.prevout.n)); diff --git a/src/test-komodo/test_parse_notarisation.cpp b/src/test-komodo/test_parse_notarisation.cpp index a61ca2aa3..2910e2f96 100644 --- a/src/test-komodo/test_parse_notarisation.cpp +++ b/src/test-komodo/test_parse_notarisation.cpp @@ -29,6 +29,26 @@ TEST(TestParseNotarisation, test__) ASSERT_FALSE(E_UNMARSHAL(opret, ss >> nd)); } +TEST(TestParseNotarisation, test__a) +{ + // be55101e6c5a93fb3611a44bd66217ad8714d204275ea4e691cfff9d65dff85c TXSCL + std::vector opret = ParseHex("fb9ea2818eec8b07f8811bab49d64379db074db478997f8114666f239bd79803cc460000d0fac4e715b7e2b917a5d79f85ece0c423d27bd3648fd39ac1dc7db8e1bd4b16545853434c00a69eab9f23d7fb63c4624973e7a9079d6ada2f327040936356d7af5e849f6d670a0003001caf7b7b9e1c9bc59d0c7a619c9683ab1dd0794b6f3ea184a19f8fda031150e700000000"); + NotarisationData nd(1); + bool res = E_UNMARSHAL(opret, ss >> nd); + ASSERT_TRUE(res); +} + +TEST(TestParseNotarisation, test__b) +{ + // 03085dafed656aaebfda25bf43ffe9d1fb72565bb1fc8b2a12a631659f28f877 TXSCL + std::vector opret = ParseHex("48c71a10aa060eab1a43f52acefac3b81fb2a2ce310186b06141884c0501d403c246000052e6d49afd82d9ab3d97c996dd9b6a78a554ffa1625e8dadf0494bd1f8442e3e545853434c007cc5c07e3b67520fd14e23cd5b49f2aa022f411500fd3326ff91e6dc0544a1c90c0003008b69117bb1376ac8df960f785d8c208c599d3a36248c98728256bb6d4737e59600000000"); + NotarisationData nd(1); + bool res = E_UNMARSHAL(opret, ss >> nd); + ASSERT_TRUE(res); +} + + + // for l in `g 'parse notarisation' ~/.komodo/debug.log | pyline 'l.split()[8]'`; do hoek decodeTx '{"hex":"'`src/komodo-cli getrawtransaction "$l"`'"}' | jq '.outputs[1].script.op_return' | pyline 'import base64; print base64.b64decode(l).encode("hex")'; done } From 846384769c22891934a7f0d56d187de6ab428182 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Mon, 11 Jun 2018 17:29:16 +0200 Subject: [PATCH 22/38] log notarisations written to index --- src/main.cpp | 28 ++++++++++++++++++---------- src/notarisationdb.cpp | 13 ++++++------- src/notarisationdb.h | 4 ++-- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 714d8625b..d8dc2ddf1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2486,8 +2486,12 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // Delete from notarisations cache NotarisationsInBlock nibs; if (GetBlockNotarisations(block.GetHash(), nibs)) { - pnotarisations->Erase(block.GetHash()); - EraseBackNotarisations(nibs); + CLevelDBBatch batch; + batch.Erase(block.GetHash()); + EraseBackNotarisations(nibs, batch); + pnotarisations->WriteBatch(batch, true); + LogPrintf("ConnectBlock: Deleted %i block notarisations in block: %s\n", + nibs.size(), block.GetHash().GetHex().data()); } // set the old best anchor back @@ -2844,14 +2848,7 @@ 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); - if (notarisations.size() > 0) { - pnotarisations->Write(block.GetHash(), notarisations); - WriteBackNotarisations(notarisations); - } - + view.PushAnchor(tree); if (!fJustCheck) { pindex->hashAnchorEnd = tree.root(); @@ -2920,6 +2917,17 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin pindex->RaiseValidity(BLOCK_VALID_SCRIPTS); setDirtyBlockIndex.insert(pindex); } + + // Record Notarisations + NotarisationsInBlock notarisations = GetNotarisationsInBlock(block, pindex->nHeight); + if (notarisations.size() > 0) { + CLevelDBBatch batch; + batch.Write(block.GetHash(), notarisations); + WriteBackNotarisations(notarisations, batch); + pnotarisations->WriteBatch(batch, true); + LogPrintf("ConnectBlock: wrote %i block notarisations in block: %s\n", + notarisations.size(), block.GetHash().GetHex().data()); + } if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index 023588874..4b2ade793 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -61,25 +61,24 @@ bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n) /* * Write an index of KMD notarisation id -> backnotarisation */ -void WriteBackNotarisations(const NotarisationsInBlock notarisations) +void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch) { - CLevelDBBatch batch; + int wrote = 0; BOOST_FOREACH(const Notarisation &n, notarisations) { - if (!n.second.txHash.IsNull()) + if (!n.second.txHash.IsNull()) { batch.Write(n.second.txHash, n); + wrote++; + } } - pnotarisations->WriteBatch(batch, true); } -void EraseBackNotarisations(const NotarisationsInBlock notarisations) +void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch) { - CLevelDBBatch batch; BOOST_FOREACH(const Notarisation &n, notarisations) { if (!n.second.txHash.IsNull()) batch.Erase(n.second.txHash); } - pnotarisations->WriteBatch(batch, true); } diff --git a/src/notarisationdb.h b/src/notarisationdb.h index 27f979784..59c500fc4 100644 --- a/src/notarisationdb.h +++ b/src/notarisationdb.h @@ -21,7 +21,7 @@ typedef std::vector NotarisationsInBlock; NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight); bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs); bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n); -void WriteBackNotarisations(const NotarisationsInBlock notarisations); -void EraseBackNotarisations(const NotarisationsInBlock notarisations); +void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch); +void EraseBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch); #endif /* NOTARISATIONDB_H */ From 7f3cc8a2969051f30e371f635de058e911a1d4a2 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Thu, 14 Jun 2018 08:35:53 -0300 Subject: [PATCH 23/38] create function to query notarisations leveldb by height --- src/crosschain.cpp | 72 ++++++++++++++++++++++++++---------------- src/main.cpp | 2 +- src/notarisationdb.cpp | 2 +- src/notarisationdb.h | 2 +- 4 files changed, 48 insertions(+), 30 deletions(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index dd352b025..10910a48a 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -1,8 +1,11 @@ #include "cc/eval.h" +#include "crosschain.h" #include "importcoin.h" #include "main.h" #include "notarisationdb.h" -#include "komodo_structs.h" + + +int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440; /* @@ -32,7 +35,7 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh int seenOwnNotarisations = 0; - for (int i=0; i<1440; i++) { + for (int i=0; i kmdHeight) break; NotarisationsInBlock notarisations; uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock; @@ -60,6 +63,32 @@ end: } +/* + * Get a notarisation from a given height + * + * Will scan notarisations leveldb up to a limit + */ +template +int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &found) +{ + int limit = std::min(nHeight + NOTARISATION_SCAN_LIMIT_BLOCKS, chainActive.Height()); + + for (int h=nHeight; hphashBlock; + if (!GetBlockNotarisations(blockHash, notarisations)) + continue; + + BOOST_FOREACH(found, notarisations) { + if (f(found)) { + return h; + } + } + } + return 0; +} + + /* On KMD */ TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_t targetCCid, const TxProof assetChainProof) @@ -152,12 +181,13 @@ void CompleteImportTransaction(CTransaction &importTx) } -struct notarized_checkpoint *komodo_npptr_at(int idx); -struct notarized_checkpoint *komodo_npptr_for_height(int32_t height, int *idx); +bool IsSameAssetChain(const Notarisation ¬a) { + return strcmp(nota.second.symbol, ASSETCHAINS_SYMBOL) == 0; +}; /* On assetchain */ -bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, std::pair &out) +bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out) { /* * Here we are given a txid, and a proof. @@ -168,18 +198,10 @@ bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, std::pairnotarized_desttxid, out); - throw std::runtime_error("Can't get backnotarisation"); + return (bool) ScanNotarisationsFromHeight(bn.second.height+1, &IsSameAssetChain, out); } -struct notarized_checkpoint* komodo_npptr(int32_t height); - /* * On assetchain * in: txid @@ -187,9 +209,9 @@ struct notarized_checkpoint* komodo_npptr(int32_t height); */ TxProof GetAssetchainProof(uint256 hash) { - int nIndex, md; + int nIndex; CBlockIndex* blockIndex; - struct notarized_checkpoint* np; + Notarisation nota; std::vector branch; { @@ -202,20 +224,18 @@ TxProof GetAssetchainProof(uint256 hash) throw std::runtime_error("tx still in mempool"); blockIndex = mapBlockIndex[blockHash]; - if (!(np = komodo_npptr(blockIndex->nHeight))) + if (!ScanNotarisationsFromHeight(blockIndex->nHeight, &IsSameAssetChain, nota)) throw std::runtime_error("notarisation not found"); // index of block in MoM leaves - nIndex = np->notarized_height - blockIndex->nHeight; - // MoMdepth shares space with ccid - md = np->MoMdepth & 0xffff; + nIndex = nota.second.height - blockIndex->nHeight; } // build merkle chain from blocks to MoM { std::vector leaves, tree; - for (int i=0; inotarized_height - i]->hashMerkleRoot; + for (int i=0; ihashMerkleRoot; leaves.push_back(mRoot); } bool fMutated; @@ -224,7 +244,7 @@ TxProof GetAssetchainProof(uint256 hash) // Check branch uint256 ourResult = SafeCheckMerkleBranch(blockIndex->hashMerkleRoot, branch, nIndex); - if (np->MoM != ourResult) + if (nota.second.MoM != ourResult) throw std::runtime_error("Failed merkle block->MoM"); } @@ -259,12 +279,10 @@ TxProof GetAssetchainProof(uint256 hash) } // Check the proof - if (np->MoM != CBlock::CheckMerkleBranch(hash, branch, nIndex)) + if (nota.second.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(np->notarized_desttxid, MerkleBranch(nIndex, branch)); + return std::make_pair(nota.second.txHash, MerkleBranch(nIndex, branch)); } - - diff --git a/src/main.cpp b/src/main.cpp index d8dc2ddf1..b67b1c478 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2919,7 +2919,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } // Record Notarisations - NotarisationsInBlock notarisations = GetNotarisationsInBlock(block, pindex->nHeight); + NotarisationsInBlock notarisations = ScanBlockNotarisations(block, pindex->nHeight); if (notarisations.size() > 0) { CLevelDBBatch batch; batch.Write(block.GetHash(), notarisations); diff --git a/src/notarisationdb.cpp b/src/notarisationdb.cpp index 4b2ade793..6210d88dd 100644 --- a/src/notarisationdb.cpp +++ b/src/notarisationdb.cpp @@ -12,7 +12,7 @@ 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) +NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight) { EvalRef eval; NotarisationsInBlock vNotarisations; diff --git a/src/notarisationdb.h b/src/notarisationdb.h index 59c500fc4..ce5360e7d 100644 --- a/src/notarisationdb.h +++ b/src/notarisationdb.h @@ -18,7 +18,7 @@ extern NotarisationDB *pnotarisations; typedef std::pair Notarisation; typedef std::vector NotarisationsInBlock; -NotarisationsInBlock GetNotarisationsInBlock(const CBlock &block, int nHeight); +NotarisationsInBlock ScanBlockNotarisations(const CBlock &block, int nHeight); bool GetBlockNotarisations(uint256 blockHash, NotarisationsInBlock &nibs); bool GetBackNotarisation(uint256 notarisationHash, Notarisation &n); void WriteBackNotarisations(const NotarisationsInBlock notarisations, CLevelDBBatch &batch); From 84964112be9ff72f67692580302939b8f65b49f2 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sat, 16 Jun 2018 12:05:22 -0300 Subject: [PATCH 24/38] move disconnect notarisations from DisconnectBlock to DisconnectTip --- src/crosschain.cpp | 4 ++-- src/main.cpp | 54 ++++++++++++++++++++++++++++------------------ 2 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 10910a48a..ddceb1b9b 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -75,8 +75,8 @@ int ScanNotarisationsFromHeight(int nHeight, const IsTarget f, Notarisation &fou for (int h=nHeight; hphashBlock; - if (!GetBlockNotarisations(blockHash, notarisations)) + + if (!GetBlockNotarisations(*chainActive[h]->phashBlock, notarisations)) continue; BOOST_FOREACH(found, notarisations) { diff --git a/src/main.cpp b/src/main.cpp index b67b1c478..563608cbf 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2349,6 +2349,37 @@ static bool ApplyTxInUndo(const CTxInUndo& undo, CCoinsViewCache& view, const CO return fClean; } + +void ConnectNotarisations(const CBlock &block, int height) +{ + // Record Notarisations + NotarisationsInBlock notarisations = ScanBlockNotarisations(block, height); + if (notarisations.size() > 0) { + CLevelDBBatch batch; + batch.Write(block.GetHash(), notarisations); + WriteBackNotarisations(notarisations, batch); + pnotarisations->WriteBatch(batch, true); + LogPrintf("ConnectBlock: wrote %i block notarisations in block: %s\n", + notarisations.size(), block.GetHash().GetHex().data()); + } +} + + +void DisconnectNotarisations(const CBlock &block) +{ + // Delete from notarisations cache + NotarisationsInBlock nibs; + if (GetBlockNotarisations(block.GetHash(), nibs)) { + CLevelDBBatch batch; + batch.Erase(block.GetHash()); + EraseBackNotarisations(nibs, batch); + pnotarisations->WriteBatch(batch, true); + LogPrintf("DisconnectTip: deleted %i block notarisations in block: %s\n", + nibs.size(), block.GetHash().GetHex().data()); + } +} + + bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex, CCoinsViewCache& view, bool* pfClean) { assert(pindex->GetBlockHash() == view.GetBestBlock()); @@ -2483,17 +2514,6 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } } - // Delete from notarisations cache - NotarisationsInBlock nibs; - if (GetBlockNotarisations(block.GetHash(), nibs)) { - CLevelDBBatch batch; - batch.Erase(block.GetHash()); - EraseBackNotarisations(nibs, batch); - pnotarisations->WriteBatch(batch, true); - LogPrintf("ConnectBlock: Deleted %i block notarisations in block: %s\n", - nibs.size(), block.GetHash().GetHex().data()); - } - // set the old best anchor back view.PopAnchor(blockUndo.old_tree_root); @@ -2918,16 +2938,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin setDirtyBlockIndex.insert(pindex); } - // Record Notarisations - NotarisationsInBlock notarisations = ScanBlockNotarisations(block, pindex->nHeight); - if (notarisations.size() > 0) { - CLevelDBBatch batch; - batch.Write(block.GetHash(), notarisations); - WriteBackNotarisations(notarisations, batch); - pnotarisations->WriteBatch(batch, true); - LogPrintf("ConnectBlock: wrote %i block notarisations in block: %s\n", - notarisations.size(), block.GetHash().GetHex().data()); - } + ConnectNotarisations(block, pindex->nHeight); if (fTxIndex) if (!pblocktree->WriteTxIndex(vPos)) @@ -3165,6 +3176,7 @@ bool static DisconnectTip(CValidationState &state, bool fBare = false) { if (!DisconnectBlock(block, state, pindexDelete, view)) return error("DisconnectTip(): DisconnectBlock %s failed", pindexDelete->GetBlockHash().ToString()); assert(view.Flush()); + DisconnectNotarisations(block); } LogPrint("bench", "- Disconnect block: %.2fms\n", (GetTimeMicros() - nStart) * 0.001); uint256 anchorAfterDisconnect = pcoinsTip->GetBestAnchor(); From 36f8002f93cadbc143bd619390079b5976fcb88d Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Sun, 17 Jun 2018 17:20:20 -0300 Subject: [PATCH 25/38] fix GetNextBacknotarisation --- src/crosschain.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index ddceb1b9b..d6493f35d 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -198,7 +198,16 @@ bool GetNextBacknotarisation(uint256 kmdNotarisationTxid, Notarisation &out) if (!GetBackNotarisation(kmdNotarisationTxid, bn)) return false; - return (bool) ScanNotarisationsFromHeight(bn.second.height+1, &IsSameAssetChain, out); + // Need to get block height of that backnotarisation + EvalRef eval; + CBlockIndex block; + CTransaction tx; + if (!eval->GetTxConfirmed(bn.first, tx, block)){ + fprintf(stderr, "Can't get height of backnotarisation, this should not happen\n"); + return false; + } + + return (bool) ScanNotarisationsFromHeight(block.nHeight+1, &IsSameAssetChain, out); } From 469bd6378f9fa2189f34f5f26608c8ccccf4d590 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Tue, 19 Jun 2018 12:45:42 -0300 Subject: [PATCH 26/38] fix momom generation to inclusive or exclusive of whole blocks --- src/crosschain.cpp | 54 +++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 15 deletions(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index d6493f35d..4f19f0eb2 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -23,7 +23,7 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh * previous notarisation. * * kmdHeight notarisations-0 notarisations-1 - * | |********************| + * *********************| * > scan backwards > */ @@ -41,20 +41,25 @@ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeigh uint256 blockHash = *chainActive[kmdHeight-i]->phashBlock; if (!GetBlockNotarisations(blockHash, notarisations)) continue; + + // See if we have an own notarisation in this block BOOST_FOREACH(Notarisation& nota, notarisations) { - NotarisationData& data = nota.second; - if (data.ccId != targetCCid) - continue; - if (strcmp(data.symbol, symbol) == 0) + if (strcmp(nota.second.symbol, symbol) == 0) { seenOwnNotarisations++; - if (seenOwnNotarisations == 2) - goto end; if (seenOwnNotarisations == 1) destNotarisationTxid = nota.first; + else if (seenOwnNotarisations == 2) + goto end; + break; + } + } + + if (seenOwnNotarisations == 1) { + BOOST_FOREACH(Notarisation& nota, notarisations) { + if (nota.second.ccId == targetCCid) + moms.push_back(nota.second.MoM); } - if (seenOwnNotarisations == 1) - moms.push_back(data.MoM); } } @@ -108,14 +113,25 @@ TxProof GetCrossChainProof(const uint256 txid, const char* targetSymbol, uint32_ CTransaction sourceNotarisation; uint256 hashBlock; CBlockIndex blockIdx; - if (eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx)) - kmdHeight = blockIdx.nHeight; - else if (eval->GetTxUnconfirmed(assetChainProof.first, sourceNotarisation, hashBlock)) - kmdHeight = chainActive.Tip()->nHeight; - else + if (!eval->GetTxConfirmed(assetChainProof.first, sourceNotarisation, blockIdx)) throw std::runtime_error("Notarisation not found"); + kmdHeight = blockIdx.nHeight; } + // We now have a kmdHeight of the notarisation from chain A. So we know that a MoM exists + // at that height. + // If we call CalculateProofRoot with that height, it'll scan backwards, until it finds + // a notarisation from B, and it might not include our notarisation from A + // at all. So, the thing we need to do is scan forwards to find the notarisation for B, + // that is inclusive of A. + Notarisation nota; + auto isTarget = [&](Notarisation ¬a) { + return strcmp(nota.second.symbol, targetSymbol) == 0; + }; + kmdHeight = ScanNotarisationsFromHeight(kmdHeight, isTarget, nota); + if (!kmdHeight) + throw std::runtime_error("Cannot find notarisation for target inclusive of source"); + // Get MoMs for kmd height and symbol std::vector moms; uint256 targetChainNotarisationTxid; @@ -233,7 +249,15 @@ TxProof GetAssetchainProof(uint256 hash) throw std::runtime_error("tx still in mempool"); blockIndex = mapBlockIndex[blockHash]; - if (!ScanNotarisationsFromHeight(blockIndex->nHeight, &IsSameAssetChain, nota)) + int h = blockIndex->nHeight; + // The assumption here is that the first notarisation for a height GTE than + // the transaction block height will contain the corresponding MoM. If there + // are sequence issues with the notarisations this may fail. + auto isTarget = [&](Notarisation ¬a) { + if (!IsSameAssetChain(nota)) return false; + return nota.second.height >= blockIndex->nHeight; + }; + if (!ScanNotarisationsFromHeight(blockIndex->nHeight, isTarget, nota)) throw std::runtime_error("notarisation not found"); // index of block in MoM leaves From 87d08c5a45602534ceaea7e97bd02a660bc663f1 Mon Sep 17 00:00:00 2001 From: Scott Sadler Date: Tue, 19 Jun 2018 20:39:04 -0300 Subject: [PATCH 27/38] some comments --- src/crosschain.cpp | 25 ++++++++++++++++++++----- src/rpccrosschain.cpp | 3 +-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/crosschain.cpp b/src/crosschain.cpp index 4f19f0eb2..ca7223abb 100644 --- a/src/crosschain.cpp +++ b/src/crosschain.cpp @@ -4,14 +4,29 @@ #include "main.h" #include "notarisationdb.h" +/* + * The crosschain workflow. + * + * 3 chains, A, B, and KMD. We would like to prove TX on B. + * There is a notarisation, nA0, which will include TX via an MoM. + * The notarisation nA0 must fall between 2 notarisations of B, + * ie, nB0 and nB1. An MoMoM including this range is propagated to + * B in notarisation receipt (backnotarisation) bnB2. + * + * A: TX bnA0 + * \ / + * KMD: nB0 nA0 nB1 nB2 + * \ \ \ + * B: bnB0 bnB1 bnB2 + */ + +// XXX: There are potential crashes wherever we access chainActive without a lock, +// because it might be disconnecting blocks at the same time. + int NOTARISATION_SCAN_LIMIT_BLOCKS = 1440; -/* - * This file is built in the server - */ - /* On KMD */ uint256 CalculateProofRoot(const char* symbol, uint32_t targetCCid, int kmdHeight, std::vector &moms, uint256 &destNotarisationTxid) @@ -258,7 +273,7 @@ TxProof GetAssetchainProof(uint256 hash) return nota.second.height >= blockIndex->nHeight; }; if (!ScanNotarisationsFromHeight(blockIndex->nHeight, isTarget, nota)) - throw std::runtime_error("notarisation not found"); + throw std::runtime_error("backnotarisation not yet confirmed"); // index of block in MoM leaves nIndex = nota.second.height - blockIndex->nHeight; diff --git a/src/rpccrosschain.cpp b/src/rpccrosschain.cpp index c99581406..35542613a 100644 --- a/src/rpccrosschain.cpp +++ b/src/rpccrosschain.cpp @@ -19,11 +19,10 @@ #include "script/standard.h" #include - #include - #include + using namespace std; 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); From 8154603bf337c418e5ebc2635e0a4fa0cbb6d751 Mon Sep 17 00:00:00 2001 From: jl777 Date: Wed, 20 Jun 2018 05:03:31 -1100 Subject: [PATCH 28/38] Change entropy source for PoS to blockhash[-50] --- src/komodo_bitcoind.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 1b35918c5..8eed2572b 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1100,7 +1100,7 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh if ( (minage= nHeight*3) > 6000 ) minage = 6000; pindex = 0; - if ( (pindex= komodo_chainactive(nHeight>200?nHeight-200:1)) != 0 ) + if ( (pindex= komodo_chainactive(nHeight>50?nHeight-50:1)) != 0 ) { vcalc_sha256(0,(uint8_t *)&addrhash,(uint8_t *)address,(int32_t)strlen(address)); segid = ((nHeight + addrhash.uints[0]) & 0x3f); From 4c36b53066e5a15896f556f1cbf9ab535868aacb Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Jun 2018 07:27:09 -1100 Subject: [PATCH 29/38] +VRSC +RFOX --- src/assetchains.old | 3 ++- src/dpowassets | 2 ++ src/fiat-cli | 2 ++ src/fiat/rfox | 2 ++ src/fiat/vrsc | 2 ++ src/komodo_bitcoind.h | 2 +- 6 files changed, 11 insertions(+), 2 deletions(-) create mode 100755 src/fiat/rfox create mode 100755 src/fiat/vrsc diff --git a/src/assetchains.old b/src/assetchains.old index 3f42fef13..e6d019168 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -35,6 +35,7 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=GLXT -ac_supply=100000000 -addnode=13.230.224.15 & ./komodod -pubkey=$pubkey -ac_name=EQL -ac_supply=500000000 -addnode=46.101.124.153 & ./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -addnode=54.39.23.248 & -~/VerusCoin/src/komodod -ac_name=VRSC -ac_algo=verushash -ac_cc=1 -ac_veruspos=50 -ac_supply=0 -ac_eras=3 -ac_reward=0,38400000000,2400000000 -ac_halving=1,43200,1051920 -ac_decay=100000000,0,0 -ac_end=10080,226080,0 -ac_timelockgte=19200000000 -ac_timeunlockfrom=129600 -ac_timeunlockto=1180800 -addnode=185.25.48.236 -addnode=185.64.105.111 & +./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 78.47.196.146 & +~/veruscoin/src/komodod -ac_name=VRSC -ac_algo=verushash -ac_cc=1 -ac_veruspos=50 -ac_supply=0 -ac_eras=3 -ac_reward=0,38400000000,2400000000 -ac_halving=1,43200,1051920 -ac_decay=100000000,0,0 -ac_end=10080,226080,0 -ac_timelockgte=19200000000 -ac_timeunlockfrom=129600 -ac_timeunlockto=1180800 -addnode=185.25.48.236 -addnode=185.64.105.111 & diff --git a/src/dpowassets b/src/dpowassets index b15bdbbd3..f9d86dd57 100755 --- a/src/dpowassets +++ b/src/dpowassets @@ -36,3 +36,5 @@ curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dp curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"ZILLA\",\"pubkey\":\"$pubkey\"}" curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"CHIPS\",\"pubkey\":\"$pubkey\"}" curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"GAME\",\"freq\":5,\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"RFOX\",\"freq\":10,\"pubkey\":\"$pubkey\"}" +curl --url "http://127.0.0.1:7776" --data "{\"agent\":\"iguana\",\"method\":\"dpow\",\"symbol\":\"VRSC\",\"freq\":10,\"pubkey\":\"$pubkey\"}" diff --git a/src/fiat-cli b/src/fiat-cli index 4e1c61675..910246e6c 100755 --- a/src/fiat-cli +++ b/src/fiat-cli @@ -31,3 +31,5 @@ echo dsec; fiat/dsec $1 $2 $3 $4 echo glxt; fiat/glxt $1 $2 $3 $4 echo eql; fiat/eql $1 $2 $3 $4 echo zilla; fiat/zilla $1 $2 $3 $4 +echo vrsc; fiat/zilla $1 $2 $3 $4 +echo rfox; fiat/zilla $1 $2 $3 $4 diff --git a/src/fiat/rfox b/src/fiat/rfox new file mode 100755 index 000000000..e082505d0 --- /dev/null +++ b/src/fiat/rfox @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=RFOX $1 $2 $3 $4 $5 $6 diff --git a/src/fiat/vrsc b/src/fiat/vrsc new file mode 100755 index 000000000..6cd4dd1fb --- /dev/null +++ b/src/fiat/vrsc @@ -0,0 +1,2 @@ +#!/bin/bash +./komodo-cli -ac_name=VRSC $1 $2 $3 $4 $5 $6 diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 8eed2572b..5a41c2bfc 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -1097,7 +1097,7 @@ uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeigh fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime); return(0); } - if ( (minage= nHeight*3) > 6000 ) + if ( (minage= nHeight*3) > 6000 ) // about 100 blocks minage = 6000; pindex = 0; if ( (pindex= komodo_chainactive(nHeight>50?nHeight-50:1)) != 0 ) From ce3cd49fb7e91ba297a1fc4c1bd6f3230183e1f8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Jun 2018 07:57:56 -1100 Subject: [PATCH 30/38] Fix zilla --- src/fiat-cli | 4 ++-- src/fiat/zilla | 0 2 files changed, 2 insertions(+), 2 deletions(-) mode change 100644 => 100755 src/fiat/zilla diff --git a/src/fiat-cli b/src/fiat-cli index 910246e6c..2838575f9 100755 --- a/src/fiat-cli +++ b/src/fiat-cli @@ -31,5 +31,5 @@ echo dsec; fiat/dsec $1 $2 $3 $4 echo glxt; fiat/glxt $1 $2 $3 $4 echo eql; fiat/eql $1 $2 $3 $4 echo zilla; fiat/zilla $1 $2 $3 $4 -echo vrsc; fiat/zilla $1 $2 $3 $4 -echo rfox; fiat/zilla $1 $2 $3 $4 +echo vrsc; fiat/vrsc $1 $2 $3 $4 +echo rfox; fiat/rfox $1 $2 $3 $4 diff --git a/src/fiat/zilla b/src/fiat/zilla old mode 100644 new mode 100755 From 9d13e5c842de93c4d215fa09bf6145a3237c4315 Mon Sep 17 00:00:00 2001 From: jl777 Date: Thu, 21 Jun 2018 08:00:43 -1100 Subject: [PATCH 31/38] -addnode --- src/assetchains.old | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/assetchains.old b/src/assetchains.old index e6d019168..228767f25 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -35,7 +35,7 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=GLXT -ac_supply=100000000 -addnode=13.230.224.15 & ./komodod -pubkey=$pubkey -ac_name=EQL -ac_supply=500000000 -addnode=46.101.124.153 & ./komodod -pubkey=$pubkey -ac_name=ZILLA -ac_supply=11000000 -addnode=54.39.23.248 & -./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 78.47.196.146 & +./komodod -pubkey=$pubkey -ac_name=RFOX -ac_supply=1000000000 -ac_reward=100000000 -addnode=78.47.196.146 & ~/veruscoin/src/komodod -ac_name=VRSC -ac_algo=verushash -ac_cc=1 -ac_veruspos=50 -ac_supply=0 -ac_eras=3 -ac_reward=0,38400000000,2400000000 -ac_halving=1,43200,1051920 -ac_decay=100000000,0,0 -ac_end=10080,226080,0 -ac_timelockgte=19200000000 -ac_timeunlockfrom=129600 -ac_timeunlockto=1180800 -addnode=185.25.48.236 -addnode=185.64.105.111 & From ade63fa9491e771fef34113e9674c85bafabb440 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Jun 2018 02:57:08 -1100 Subject: [PATCH 32/38] +print --- src/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 2466928c1..52651b7f4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2832,7 +2832,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (fAddressIndex) { for (unsigned int k = 0; k < tx.vout.size(); k++) { const CTxOut &out = tx.vout[k]; - +fprintf(stderr,"add %d vouts\n",(int32_t)tx.vout.size()); if (out.scriptPubKey.IsPayToScriptHash()) { vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); From ba31c2f4b4205942f1ea92bdd37b10eb596d5765 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Jun 2018 03:04:06 -1100 Subject: [PATCH 33/38] Fix pay to pubkey for -addressindex --- src/main.cpp | 10 +++++----- src/rpcblockchain.cpp | 2 +- src/txmempool.cpp | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 52651b7f4..6aed822e5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2430,7 +2430,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), hash, k), CAddressUnspentValue())); } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); // undo receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); @@ -2503,7 +2503,7 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); + vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); // undo spending activity addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); @@ -2784,7 +2784,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin hashBytes = uint160(vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); addressType = 2; } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - hashBytes = uint160(vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); + hashBytes = uint160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); addressType = 1; } else { hashBytes.SetNull(); @@ -2832,7 +2832,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (fAddressIndex) { for (unsigned int k = 0; k < tx.vout.size(); k++) { const CTxOut &out = tx.vout[k]; -fprintf(stderr,"add %d vouts\n",(int32_t)tx.vout.size()); +//fprintf(stderr,"add %d vouts\n",(int32_t)tx.vout.size()); if (out.scriptPubKey.IsPayToScriptHash()) { vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); @@ -2843,7 +2843,7 @@ fprintf(stderr,"add %d vouts\n",(int32_t)tx.vout.size()); addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); // record receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue)); diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index ebb706167..0a47339fa 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -204,7 +204,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString())); } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); } else { continue; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index f2bf39c29..c7dafb5f4 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -138,7 +138,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC mapAddress.insert(make_pair(key, delta)); inserted.push_back(key); } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); + vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1); CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(make_pair(key, delta)); @@ -154,7 +154,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); + vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); std::pair ret; CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0); mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); @@ -213,7 +213,7 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac addressHash = uint160(vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); addressType = 2; } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - addressHash = uint160(vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); + addressHash = uint160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); addressType = 1; } else { addressHash.SetNull(); From fa7bf712a164da7020d62d4b7b0ce9b015913fc9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Jun 2018 04:40:32 -1100 Subject: [PATCH 34/38] Add support for pay2pubkey for -addressindex --- src/main.cpp | 66 ++++++++++++++++++++++++++++++++------- src/rpcblockchain.cpp | 26 ++++++++++++--- src/rpcmisc.cpp | 6 +++- src/rpcrawtransaction.cpp | 6 +++- src/script/script.cpp | 8 +++++ src/script/script.h | 1 + src/txmempool.cpp | 36 ++++++++++++++++----- 7 files changed, 123 insertions(+), 26 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6aed822e5..5a1cdcd10 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2429,8 +2429,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // undo unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), hash, k), CAddressUnspentValue())); - } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + } + else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); // undo receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); @@ -2438,7 +2439,18 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // undo unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), hash, k), CAddressUnspentValue())); - } else { + } + else if (out.scriptPubKey.IsPayToPublicKey()) { + vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + + // undo receiving activity + addressIndex.push_back(make_pair(CAddressIndexKey(3, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); + + // undo unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(3, uint160(hashBytes), hash, k), CAddressUnspentValue())); + + } + else { continue; } @@ -2502,8 +2514,9 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); - } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); + } + else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); // undo spending activity addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); @@ -2511,7 +2524,18 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex // restore unspent index addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); - } else { + } + else if (prevout.scriptPubKey.IsPayToPublicKey()) { + vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); + + // undo spending activity + addressIndex.push_back(make_pair(CAddressIndexKey(3, Hash160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); + + // restore unspent index + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(3, Hash160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); + + } + else { continue; } } @@ -2783,10 +2807,16 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin if (prevout.scriptPubKey.IsPayToScriptHash()) { hashBytes = uint160(vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); addressType = 2; - } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - hashBytes = uint160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); + } + else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + hashBytes = uint160(vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); addressType = 1; - } else { + } + else if (prevout.scriptPubKey.IsPayToPublicKey()) { + hashBytes = uint160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); + addressType = 3; + } + else { hashBytes.SetNull(); addressType = 0; } @@ -2842,8 +2872,9 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // record unspent output addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(2, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); - } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + } + else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); // record receiving activity addressIndex.push_back(make_pair(CAddressIndexKey(1, uint160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue)); @@ -2851,7 +2882,18 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin // record unspent output addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, uint160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); - } else { + } + else if (out.scriptPubKey.IsPayToPublicKey()) { + vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + + // record receiving activity + addressIndex.push_back(make_pair(CAddressIndexKey(3, Hash160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue)); + + // record unspent output + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(3, Hash160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); + + } + else { continue; } diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 0a47339fa..ca9a21278 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -172,9 +172,14 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) if (GetSpentIndex(spentKey, spentInfo)) { if (spentInfo.addressType == 1) { delta.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); - } else if (spentInfo.addressType == 2) { + } + else if (spentInfo.addressType == 2) { delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); - } else { + } + else if (spentInfo.addressType == 3) { + xxx delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); + } + else { continue; } delta.push_back(Pair("satoshis", -1 * spentInfo.satoshis)); @@ -203,10 +208,21 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) vector hashBytes(out.scriptPubKey.begin()+2, out.scriptPubKey.begin()+22); delta.push_back(Pair("address", CBitcoinAddress(CScriptID(uint160(hashBytes))).ToString())); - } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + } + else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); - } else { + } + else if (out.scriptPubKey.IsPayToPublicKey()) { + CTxDestination address; + if (ExtractDestination(out.scriptPubKey, address)) + { + //vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + //xxx delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); + delta.push_back(Pair("address", address.ToString())); + } + } + else { continue; } diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 3cc18c6a6..67518c09c 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -603,7 +603,11 @@ bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &addr address = CBitcoinAddress(CScriptID(hash)).ToString(); } else if (type == 1) { address = CBitcoinAddress(CKeyID(hash)).ToString(); - } else { + } + else if (type == 3) { + xxx address = CBitcoinAddress(CKeyID(hash)).ToString(); + } + else { return false; } return true; diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index 91dff8bf9..ff6df3505 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -161,9 +161,13 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& in.push_back(Pair("valueSat", spentInfo.satoshis)); if (spentInfo.addressType == 1) { in.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); - } else if (spentInfo.addressType == 2) { + } + else if (spentInfo.addressType == 2) { in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); } + else if (spentInfo.addressType == 3) { + xxx in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); + } } } in.push_back(Pair("sequence", (int64_t)txin.nSequence)); diff --git a/src/script/script.cpp b/src/script/script.cpp index 160c57016..4cffc6c5a 100644 --- a/src/script/script.cpp +++ b/src/script/script.cpp @@ -229,6 +229,14 @@ bool CScript::IsPayToPublicKeyHash() const (*this)[24] == OP_CHECKSIG); } +bool CScript::IsPayToPublicKey() const +{ + // Extra-fast test for pay-to-pubkey CScripts: + return (this->size() == 35 && + (*this)[0] == 33 && + (*this)[34] == OP_CHECKSIG); +} + bool CScript::IsPayToScriptHash() const { // Extra-fast test for pay-to-script-hash CScripts: diff --git a/src/script/script.h b/src/script/script.h index 864a92f1f..56d2ff0b6 100644 --- a/src/script/script.h +++ b/src/script/script.h @@ -567,6 +567,7 @@ public: unsigned int GetSigOpCount(const CScript& scriptSig) const; bool IsPayToPublicKeyHash() const; + bool IsPayToPublicKey() const; bool IsPayToScriptHash() const; bool IsPayToCryptoCondition() const; diff --git a/src/txmempool.cpp b/src/txmempool.cpp index c7dafb5f4..67e79b5cf 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -137,13 +137,21 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(make_pair(key, delta)); inserted.push_back(key); - } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); + } + else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23); CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, j, 1); CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(make_pair(key, delta)); inserted.push_back(key); } + else if (prevout.scriptPubKey.IsPayToPublicKey()) { + vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); + CMempoolAddressDeltaKey key(3, Hash160(hashBytes), txhash, j, 1); + CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); + mapAddress.insert(make_pair(key, delta)); + inserted.push_back(key); + } } for (unsigned int k = 0; k < tx.vout.size(); k++) { @@ -153,13 +161,21 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC CMempoolAddressDeltaKey key(2, uint160(hashBytes), txhash, k, 0); mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); - } else if (out.scriptPubKey.IsPayToPublicKeyHash()) { - vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + } + else if (out.scriptPubKey.IsPayToPublicKeyHash()) { + vector hashBytes(out.scriptPubKey.begin()+3, out.scriptPubKey.begin()+23); std::pair ret; CMempoolAddressDeltaKey key(1, uint160(hashBytes), txhash, k, 0); mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); } + else if (out.scriptPubKey.IsPayToPublicKey()) { + vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); + std::pair ret; + CMempoolAddressDeltaKey key(3, Hash160(hashBytes), txhash, k, 0); + mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); + inserted.push_back(key); + } } mapAddressInserted.insert(make_pair(txhash, inserted)); @@ -212,10 +228,16 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac if (prevout.scriptPubKey.IsPayToScriptHash()) { addressHash = uint160(vector (prevout.scriptPubKey.begin()+2, prevout.scriptPubKey.begin()+22)); addressType = 2; - } else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { - addressHash = uint160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); + } + else if (prevout.scriptPubKey.IsPayToPublicKeyHash()) { + addressHash = uint160(vector (prevout.scriptPubKey.begin()+3, prevout.scriptPubKey.begin()+23)); addressType = 1; - } else { + } + else if (prevout.scriptPubKey.IsPayToPublicKey()) { + addressHash = Hash160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); + addressType = 3; + } + else { addressHash.SetNull(); addressType = 0; } From dc276bd08718349f69f6cf47e80bb5b97295647d Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Jun 2018 06:18:12 -1100 Subject: [PATCH 35/38] Map address type 3 -> 1 --- src/main.cpp | 10 +++++----- src/rpcblockchain.cpp | 3 --- src/rpcrawtransaction.cpp | 3 --- src/txmempool.cpp | 6 +++--- 4 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 5a1cdcd10..bb1f959ad 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2529,10 +2529,10 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); // undo spending activity - addressIndex.push_back(make_pair(CAddressIndexKey(3, Hash160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); + addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->nHeight, i, hash, j, true), prevout.nValue * -1)); // restore unspent index - addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(3, Hash160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), input.prevout.hash, input.prevout.n), CAddressUnspentValue(prevout.nValue, prevout.scriptPubKey, undo.nHeight))); } else { @@ -2814,7 +2814,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin } else if (prevout.scriptPubKey.IsPayToPublicKey()) { hashBytes = uint160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); - addressType = 3; + addressType = 1; } else { hashBytes.SetNull(); @@ -2887,10 +2887,10 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); // record receiving activity - addressIndex.push_back(make_pair(CAddressIndexKey(3, Hash160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue)); + addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->nHeight, i, txhash, k, false), out.nValue)); // record unspent output - addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(3, Hash160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), txhash, k), CAddressUnspentValue(out.nValue, out.scriptPubKey, pindex->nHeight))); } else { diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index ca9a21278..28f7dc38c 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -176,9 +176,6 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) else if (spentInfo.addressType == 2) { delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); } - else if (spentInfo.addressType == 3) { - xxx delta.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); - } else { continue; } diff --git a/src/rpcrawtransaction.cpp b/src/rpcrawtransaction.cpp index ff6df3505..a9aff1b7f 100644 --- a/src/rpcrawtransaction.cpp +++ b/src/rpcrawtransaction.cpp @@ -165,9 +165,6 @@ void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& else if (spentInfo.addressType == 2) { in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); } - else if (spentInfo.addressType == 3) { - xxx in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); - } } } in.push_back(Pair("sequence", (int64_t)txin.nSequence)); diff --git a/src/txmempool.cpp b/src/txmempool.cpp index 67e79b5cf..51ed1103e 100644 --- a/src/txmempool.cpp +++ b/src/txmempool.cpp @@ -147,7 +147,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC } else if (prevout.scriptPubKey.IsPayToPublicKey()) { vector hashBytes(prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34); - CMempoolAddressDeltaKey key(3, Hash160(hashBytes), txhash, j, 1); + CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, j, 1); CMempoolAddressDelta delta(entry.GetTime(), prevout.nValue * -1, input.prevout.hash, input.prevout.n); mapAddress.insert(make_pair(key, delta)); inserted.push_back(key); @@ -172,7 +172,7 @@ void CTxMemPool::addAddressIndex(const CTxMemPoolEntry &entry, const CCoinsViewC else if (out.scriptPubKey.IsPayToPublicKey()) { vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); std::pair ret; - CMempoolAddressDeltaKey key(3, Hash160(hashBytes), txhash, k, 0); + CMempoolAddressDeltaKey key(1, Hash160(hashBytes), txhash, k, 0); mapAddress.insert(make_pair(key, CMempoolAddressDelta(entry.GetTime(), out.nValue))); inserted.push_back(key); } @@ -235,7 +235,7 @@ void CTxMemPool::addSpentIndex(const CTxMemPoolEntry &entry, const CCoinsViewCac } else if (prevout.scriptPubKey.IsPayToPublicKey()) { addressHash = Hash160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); - addressType = 3; + addressType = 1; } else { addressHash.SetNull(); From 98d1a795df36ac9bd3026efffc50c8b7641a2ffc Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Jun 2018 06:23:36 -1100 Subject: [PATCH 36/38] Test --- src/rpcblockchain.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rpcblockchain.cpp b/src/rpcblockchain.cpp index 28f7dc38c..42cf7f2b0 100644 --- a/src/rpcblockchain.cpp +++ b/src/rpcblockchain.cpp @@ -216,7 +216,7 @@ UniValue blockToDeltasJSON(const CBlock& block, const CBlockIndex* blockindex) { //vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); //xxx delta.push_back(Pair("address", CBitcoinAddress(CKeyID(uint160(hashBytes))).ToString())); - delta.push_back(Pair("address", address.ToString())); + delta.push_back(Pair("address", CBitcoinAddress(address).ToString())); } } else { From 17e4ca47c79f511ac0a4cd1d1e0936efec3bf1d9 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Jun 2018 06:31:38 -1100 Subject: [PATCH 37/38] Syntax --- src/rpcmisc.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/rpcmisc.cpp b/src/rpcmisc.cpp index 67518c09c..065739a9a 100644 --- a/src/rpcmisc.cpp +++ b/src/rpcmisc.cpp @@ -604,9 +604,6 @@ bool getAddressFromIndex(const int &type, const uint160 &hash, std::string &addr } else if (type == 1) { address = CBitcoinAddress(CKeyID(hash)).ToString(); } - else if (type == 3) { - xxx address = CBitcoinAddress(CKeyID(hash)).ToString(); - } else { return false; } From 31e2a04e3e64242a6fa3b4d46a631b8af8f1a1d8 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Jun 2018 06:50:37 -1100 Subject: [PATCH 38/38] Fix --- src/main.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index bb1f959ad..98fdf9452 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -2444,10 +2444,10 @@ bool DisconnectBlock(CBlock& block, CValidationState& state, CBlockIndex* pindex vector hashBytes(out.scriptPubKey.begin()+1, out.scriptPubKey.begin()+34); // undo receiving activity - addressIndex.push_back(make_pair(CAddressIndexKey(3, uint160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); + addressIndex.push_back(make_pair(CAddressIndexKey(1, Hash160(hashBytes), pindex->nHeight, i, hash, k, false), out.nValue)); // undo unspent index - addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(3, uint160(hashBytes), hash, k), CAddressUnspentValue())); + addressUnspentIndex.push_back(make_pair(CAddressUnspentKey(1, Hash160(hashBytes), hash, k), CAddressUnspentValue())); } else { @@ -2813,7 +2813,7 @@ bool ConnectBlock(const CBlock& block, CValidationState& state, CBlockIndex* pin addressType = 1; } else if (prevout.scriptPubKey.IsPayToPublicKey()) { - hashBytes = uint160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); + hashBytes = Hash160(vector (prevout.scriptPubKey.begin()+1, prevout.scriptPubKey.begin()+34)); addressType = 1; } else {